doctrine/cache · error · TypeError
Expected $time to be either an integer, an instance of…
Error message
Expected $time to be either an integer, an instance of DateInterval or null, got %s
What it means
This TypeError is thrown by CacheItem::expiresAfter() when the caller passes an expiry value that is not one of the three shapes PSR-6/PSR-16 accept for a relative TTL: an integer number of seconds, a DateInterval, or null. Because the null, DateInterval, and is_int branches are handled before it, this guard only fires on invalid argument types — e.g. a numeric string like '3600', a DateTimeImmutable, a float wrapped in a string, or an array — typically meaning the caller intended a relative expiration but supplied the wrong PHP type instead of casting to int or using DateInterval.
Solutions
- Cast numeric TTLs to int: expiresAfter((int) $ttl)
- Parse interval strings into DateInterval: new DateInterval('PT1H')
- Use expiresAt() with a DateTimeImmutable if you have an absolute deadline
Example fix
// before $item->expiresAfter($config['ttl']); // string '3600' // after $item->expiresAfter((int) $config['ttl']);
Defensive patterns
Strategy: type-guard
Validate before calling
if ($time !== null && !is_int($time) && !$time instanceof \DateInterval) { throw new InvalidArgumentException('time must be int, DateInterval or null'); } Type guard
function acceptsTtl(mixed $v): bool { return $v === null || is_int($v) || $v instanceof \DateInterval; } Try / catch
try { $item->expiresAfter($ttl); } catch (\TypeError $e) { $item->expiresAfter((int) $ttl); } Prevention
- Cast config/env TTL strings to int at load time
- Use DateInterval for 'PT1H'-style durations
- Use expiresAt with DateTime for absolute deadlines
When it happens
Trigger: Calling $item->expiresAfter($value) with a float, string like '3600' or 'PT1H', a DateTime/DateTimeImmutable, or a numeric value from config.
Common situations: Reading a TTL string from environment/config ('3600') without casting; passing a DateTime object (mixing up with expiresAt); passing a float from microtime math.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Expected $time to be either an integer, an instance of…
- Expected $expiration to be an instance of DateTimeInterface…
- Expected $expiration to be an instance of DateTimeInterface…
- Cache key must be string
- Cache key length must be greater than zero.
AI-assisted analysis of doctrine/cache@e0a9919443 (2026-09-13).
Data as JSON: /api/errors/a6a5fb7780128ac4.
Report an issue: GitHub.
Appendix: source
Thrown at lib/Doctrine/Common/Cache/Psr6/CacheItem.php:102
));
}
return $this;
}
/**
* {@inheritDoc}
*/
public function expiresAfter($time): self
{
if ($time === null) {
$this->expiry = null;
} elseif ($time instanceof DateInterval) {
$this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
} elseif (is_int($time)) {
$this->expiry = $time + microtime(true);
} else {
throw new TypeError(sprintf(
'Expected $time to be either an integer, an instance of DateInterval or null, got %s',
is_object($time) ? get_class($time) : gettype($time)
));
}
return $this;
}
/**
* @internal
*/
public function getExpiry(): ?float
{
return $this->expiry;
}
}
View on GitHub (pinned to e0a9919443)