Introduction
PHP stores session data as serialized strings. When session data is truncated (disk full, process killed), modified manually, or written with an incompatible serializer, session_start() may silently fail to restore some or all session variables. The user appears logged out, shopping cart items disappear, or application state is partially lost without any error message.
Symptoms
- Session variables are empty or missing after
session_start() Notice: unserialize(): Error at offsetin error log- Some session keys exist while others are missing
- User suddenly logged out without session expiration
session_decode()returns false silently
``` PHP Notice: unserialize(): Error at offset 42 of 256 bytes in /var/www/html/lib/SessionHandler.php on line 28
# Session file content (corrupted): # user_id|i:123;cart|a:2:{i:0;s:5:"item1";i:1;s:5:"item2 # (truncated - missing closing brace) ```
Common Causes
- Session file truncated during write (disk full, process killed)
- Concurrent requests writing to the same session
- Class definition changed between session write and read
- Session handler race conditions with file-based storage
- Mixed PHP versions using different serialization formats
Step-by-Step Fix
- 1.Detect corrupted session data:
- 2.```php
- 3.// Check raw session data before unserialization
- 4.$sessionId = session_id();
- 5.$sessionFile = ini_get('session.save_path') . '/sess_' . $sessionId;
if (file_exists($sessionFile)) { $rawData = file_get_contents($sessionFile); $expectedSize = filesize($sessionFile);
// Check for truncation (valid serialized data should end properly) $lastChar = substr(trim($rawData), -1); if ($lastChar !== ';' && $lastChar !== '}') { error_log("Corrupted session detected: $sessionId (last char: '$lastChar')"); // Clear corrupted session file_put_contents($sessionFile, ''); } }
session_start(); ```
- 1.Handle unserialize errors gracefully:
- 2.```php
- 3.// Custom error handler for session unserialization
- 4.function handleSessionErrors(): void {
- 5.set_error_handler(function ($errno, $errstr) {
- 6.if (strpos($errstr, 'unserialize') !== false) {
- 7.// Log the error
- 8.error_log("Session unserialize error: $errstr");
// Clear the session and start fresh session_destroy(); session_start();
// Notify user $_SESSION['flash'] = 'Your session was reset due to a data error.'; return true; // Suppress the PHP notice } return false; // Let other errors use default handler }, E_NOTICE | E_WARNING); }
handleSessionErrors(); session_start(); ```
- 1.Use JSON-based session serialization:
- 2.```php
- 3.// PHP 5.5.4+ supports different session serialization handlers
- 4.// php.ini
- 5.session.serialize_handler = json // Instead of default 'php'
// JSON is more resilient to corruption and human-readable // Session file content: {"user_id":123,"cart":["item1","item2"]}
// Verify the setting echo ini_get('session.serialize_handler'); // Should output "json" ```
- 1.Migrate to Redis session storage:
- 2.```php
- 3.// Redis sessions are more resilient to corruption
- 4.ini_set('session.save_handler', 'redis');
- 5.ini_set('session.save_path', 'tcp://127.0.0.1:6379?auth=secret');
session_start();
// Or with custom Redis session handler class RedisSessionHandler implements SessionHandlerInterface { private Redis $redis;
public function __construct(Redis $redis) { $this->redis = $redis; }
public function read(string $id): string { $data = $this->redis->get("sess:$id"); return $data ?: ''; }
public function write(string $id, string $data): bool { return $this->redis->setex("sess:$id", 3600, $data); }
// ... implement other interface methods } ```
- 1.Validate session data after loading:
- 2.```php
- 3.session_start();
$requiredKeys = ['user_id', 'csrf_token']; $missingKeys = array_diff($requiredKeys, array_keys($_SESSION));
if (!empty($missingKeys)) { error_log("Session missing keys: " . implode(', ', $missingKeys)); // Redirect to login or reinitialize session session_destroy(); session_start(); } ```
Prevention
- Use
session.serialize_handler = jsonfor more resilient session data - Switch to Redis or database session storage for reliability
- Implement session data validation after
session_start() - Monitor disk space to prevent session file truncation
- Set
session.gc_maxlifetimeappropriately to clean stale sessions - Use atomic session writes (Redis SETEX) to prevent partial writes
- Add session integrity checks: store a hash of session data and verify on load