doctrine/cache · error · TypeError
Expected $expiration to be an instance of DateTimeInterface…
Error message
Expected $expiration to be an instance of DateTimeInterface or null, got %s
What it means
CacheItem::expiresAt() accepts only a DateTimeInterface implementation (DateTime/DateTimeImmutable) or null; anything else triggers a TypeError. This enforces the PSR-6 contract that expiration must be an absolute point in time.
Solutions
- Convert to a DateTime object first, e.g. new DateTimeImmutable('@' . $ts) for timestamps or new DateTimeImmutable($dateString) for strings
- Use expiresAfter() instead if you have a relative TTL (int seconds or DateInterval)
- Pass null explicitly to clear the expiration rather than 0 or false
Example fix
// before
$item->expiresAt(time() + 3600); // int
// after
$item->expiresAt(new DateTimeImmutable('+1 hour')); Defensive patterns
Strategy: type-guard
Validate before calling
if ($expiration !== null && !$expiration instanceof \DateTimeInterface) { throw new InvalidArgumentException('expiration must be DateTimeInterface or null'); } Type guard
function acceptsExpiration(mixed $v): bool { return $v === null || $v instanceof \DateTimeInterface; } Try / catch
try { $item->expiresAt($expiration); } catch (\TypeError $e) { /* convert and retry */ } Prevention
- Convert timestamps/strings to DateTimeImmutable before calling expiresAt
- Use expiresAfter for relative TTLs
- Remember DateInterval is not accepted by expiresAt
When it happens
Trigger: Calling $item->expiresAt($value) where $value is an int timestamp, a date string, a DateInterval, a custom date-like object not implementing DateTimeInterface, or a string from an API.
Common situations: Passing a Unix timestamp integer (common confusion with expiresAfter); passing a date string from JSON/config without parsing; using Carbon versions/objects that do not implement DateTimeInterface (rare, Carbon does implement it).
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 $time to be either an integer, an instance of…
- 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/eab1ab5a1cec3019.
Report an issue: GitHub.
Appendix: source
Thrown at lib/Doctrine/Common/Cache/Psr6/CacheItem.php:81
*/
public function set($value): self
{
$this->value = $value;
return $this;
}
/**
* {@inheritDoc}
*/
public function expiresAt($expiration): self
{
if ($expiration === null) {
$this->expiry = null;
} elseif ($expiration instanceof DateTimeInterface) {
$this->expiry = (float) $expiration->format('U.u');
} else {
throw new TypeError(sprintf(
'Expected $expiration to be an instance of DateTimeInterface or null, got %s',
is_object($expiration) ? get_class($expiration) : gettype($expiration)
));
}
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)) {View on GitHub (pinned to e0a9919443)