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

  1. Cast numeric TTLs to int: expiresAfter((int) $ttl)
  2. Parse interval strings into DateInterval: new DateInterval('PT1H')
  3. 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

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


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)