symfony/symfony · error · Symfony\Component\Cache\Exception\InvalidArgumentException
Argument "$beta" provided to "%s::get()" must be a positive
Error message
Argument "$beta" provided to "%s::get()" must be a positive number, %f given.
What it means
doGet() implements stochastic early expiration: $beta weights how early a cached item is recomputed to avoid stampedes. beta defaults to 1.0 and any value below 0 is meaningless (it would never expire), so Symfony rejects it. The message interpolates the offending float and the concrete class via static::class.
Source
Thrown at src/Symfony/Component/Cache/Traits/ContractsTrait.php:65
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
$this->setCallbackWrapper(null);
}
}
if (null !== $callbackWrapper && !$callbackWrapper instanceof \Closure) {
$callbackWrapper = $callbackWrapper(...);
}
$previousWrapper = $this->callbackWrapper;
$this->callbackWrapper = $callbackWrapper ?? static fn (callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata, ?LoggerInterface $logger, ?float $beta = null) => $callback($item, $save);
return $previousWrapper;
}
private function doGet(AdapterInterface $pool, string $key, callable $callback, ?float $beta, ?array &$metadata = null): mixed
{
if (0 > $beta ??= 1.0) {
throw new InvalidArgumentException(\sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta));
}
static $setMetadata;
$setMetadata ??= \Closure::bind(
static function (CacheItem $item, float $startTime, ?array &$metadata) {
if ($item->expiry > $endTime = microtime(true)) {
$item->newMetadata[CacheItem::METADATA_EXPIRY] = $metadata[CacheItem::METADATA_EXPIRY] = $item->expiry;
$item->newMetadata[CacheItem::METADATA_CTIME] = $metadata[CacheItem::METADATA_CTIME] = (int) ceil(1000 * ($endTime - $startTime));
} else {
unset($metadata[CacheItem::METADATA_EXPIRY], $metadata[CacheItem::METADATA_CTIME], $metadata[CacheItem::METADATA_TAGS]);
}
},
null,
CacheItem::class
);
$this->callbackWrapper ??= LockRegistry::compute(...);View on GitHub (pinned to 698e28026c)
Solutions
- Pass beta >= 0; use 0.0 to disable early expiration, or omit it to accept the default 1.0.
- Clamp computed beta: max(0.0, $computedBeta).
- If you want indefinite lifetime, set it on the item via expiresAfter/expiresAt inside the callback, not via beta.
Example fix
// before
$cache->get('k', $cb, $grace - $now); // can be negative
// after
$cache->get('k', $cb, max(0.0, $grace - $now)); Defensive patterns
Strategy: validation
Validate before calling
$beta = max(0.0, (float) $beta);
$cache->get('key', $cb, $beta); Type guard
function validBeta(float $beta): bool { return $beta >= 0.0; } Try / catch
try { $cache->get('k', $cb, $beta); }
catch (\Symfony\Component\Cache\Exception\InvalidArgumentException $e) {
// retry with default beta
return $cache->get('k', $cb);
} Prevention
- Treat beta as a non-negative weight (0 disables early expiration).
- Clamp computed beta values to >= 0.
- Document beta semantics for teammates to prevent misuse.
When it happens
Trigger: Calling $cache->get('key', $cb, -1) explicitly; passing beta computed from arithmetic that can go negative, e.g. $cache->get('k', $cb, $ttl - $now).
Common situations: Misreading beta as 'seconds of early expiration' and passing a negative delta; copying tutorial code that uses -1 to mean 'disable'.
Related errors
- Expiration date must be an integer, a DateInterval or null,
- Cache tag must be string or object that implements __toStrin
- Cache tag length must be greater than zero.
- Cache tag "%s" contains reserved characters "%s".
- Cache key must be string, "%s" given.
AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06).
Data as JSON: /api/errors/b18dadbcd265d640.
Report an issue: GitHub.