briannesbitt/Carbon · error · InvalidFormatException
Invalid serialized value: $value
Error message
Invalid serialized value: $value
What it means
fromSerialized() runs PHP unserialize() (error-suppressed) on the payload and requires the result to be an instance of the class it was called on. If unserialize() returns false, triggers a fatal, is blocked by the allowed_classes option, or yields a different class (e.g. a Carbon stored but fromSerialized called on CarbonImmutable), Carbon throws InvalidFormatException. It exists as the single decoding gate for data produced by serialize($date).
Source
Thrown at src/Carbon/Traits/Serialization.php:96
*
* @example
* ```php
* $object = Carbon::fromSerialized($value, ['allowed_classes' => [Carbon::class, CarbonImmutable::class]]);
* ```
*
* @param \Stringable|string $value
* @param array $options example: ['allowed_classes' => [CarbonImmutable::class]]
*
* @throws InvalidFormatException
*
* @return static
*/
public static function fromSerialized($value, array $options = []): static
{
$instance = @unserialize((string) $value, $options);
if (!$instance instanceof static) {
throw new InvalidFormatException("Invalid serialized value: $value");
}
return $instance;
}
/**
* The __set_state handler.
*
* @param string|array $dump
*
* @return static
*/
#[ReturnTypeWillChange]
public static function __set_state($dump): static
{
if (\is_string($dump)) {
return static::parse($dump);
}View on GitHub (pinned to b13f05955d)
Solutions
- Stop storing PHP-serialized objects: write ->toISOString() (or format('Y-m-d H:i:s.u e O')) and read back with Carbon::parse() - ISO strings survive upgrades and cross-language use
- Flush/regenerate the stale serialized cache entries after upgrading Carbon or renaming classes
- Verify you are calling fromSerialized on the exact class (or a parent) that was serialized, and that allowed_classes includes it
- Catch InvalidFormatException and rebuild the date from a canonical fallback (e.g. created_at column or 'now') while logging the bad payload
Example fix
// before
$cache->put('since', serialize($startDate));
$since = Carbon::fromSerialized($cache->get('since'));
// after
$cache->put('since', $startDate->toISOString());
$since = Carbon::parse($cache->get('since')); Defensive patterns
Strategy: try-catch
Validate before calling
// Cheap sanity check before unserialize: serialized Carbon starts with 'O:' or 'C:'
if (!is_string($payload) || !preg_match('/^[OC]:\d+:/', $payload)) {
throw new InvalidArgumentException('Not a PHP-serialized object payload');
} Type guard
function looksSerializedObject(mixed $value): bool
{
return is_string($value) && preg_match('/^[OC]:\d+:["\\]/', $value) === 1;
} Try / catch
use Carbon\Exceptions\InvalidFormatException;
try {
$since = Carbon::fromSerialized($cached);
} catch (InvalidFormatException $e) {
$since = Carbon::parse($row['created_at']); // canonical fallback
$cache->put('since', $since->toISOString()); // heal the entry
} Prevention
- Store ISO-8601 strings, not serialize() output, for anything crossing a deploy boundary
- Version your cache keys so Carbon/PHP upgrades cannot serve old serialized payloads
- After any class rename or Carbon major upgrade, plan a cache/session flush or a migration that re-encodes dates
When it happens
Trigger: Unserialize a Carbon payload written by an older Carbon major version (class layout/renames make the payload invalid); calling Carbon::fromSerialized(json_encode($date)) with JSON instead of PHP serialize format; passing a truncated/corrupted blob from cache or DB; options ['allowed_classes' => [...]] that omit the target class; storing a Carbon instance but reading it back via a subclass that does not extend it.
Common situations: Serialized Carbon objects in Redis/session/queue payloads that break after a Carbon 2-to-3 upgrade or a project namespace move; cache entries not invalidated after deploy; mixing serialization formats (JSON on write, fromSerialized on read); security-hardened unserialize settings that disable class loading.
Related errors
- Unknown unit '$unit'.
- Invalid unit for real timestamp add/sub: '$unit'
- Unable to add unit '.var_export($originalArgs, true)
- Unsupported unit '$unit'
- $anchorDay must be greater than 0
AI-assisted analysis of briannesbitt/Carbon@b13f05955d (2026-08-17).
Data as JSON: /api/errors/443d460fb1fd9e2c.
Report an issue: GitHub.