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 offset in 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. 1.Detect corrupted session data:
  2. 2.```php
  3. 3.// Check raw session data before unserialization
  4. 4.$sessionId = session_id();
  5. 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. 1.Handle unserialize errors gracefully:
  2. 2.```php
  3. 3.// Custom error handler for session unserialization
  4. 4.function handleSessionErrors(): void {
  5. 5.set_error_handler(function ($errno, $errstr) {
  6. 6.if (strpos($errstr, 'unserialize') !== false) {
  7. 7.// Log the error
  8. 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. 1.Use JSON-based session serialization:
  2. 2.```php
  3. 3.// PHP 5.5.4+ supports different session serialization handlers
  4. 4.// php.ini
  5. 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. 1.Migrate to Redis session storage:
  2. 2.```php
  3. 3.// Redis sessions are more resilient to corruption
  4. 4.ini_set('session.save_handler', 'redis');
  5. 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. 1.Validate session data after loading:
  2. 2.```php
  3. 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 = json for 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_maxlifetime appropriately 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