sebastianbergmann/phpunit · error · InvalidArgumentException
Value for nanoseconds must not be greater than 999999999.
Error message
Value for nanoseconds must not be greater than 999999999.
What it means
Duration's constructor caps nanoseconds at 999999999; larger values mean the caller did not normalize carries into seconds. Common sources are conversions into the nanoseconds slot from total-nanosecond or microsecond values, or re-creating a Duration from its asFloat()/asString() output without splitting.
Source
Thrown at src/Event/Value/Telemetry/Duration.php:162
private function ensureNotNegative(int $value, string $type): void
{
if ($value < 0) {
throw new InvalidArgumentException(
sprintf(
'Value for %s must not be negative.',
$type,
),
);
}
}
/**
* @throws InvalidArgumentException
*/
private function ensureNanoSecondsInRange(int $nanoseconds): void
{
if ($nanoseconds > 999999999) {
throw new InvalidArgumentException(
'Value for nanoseconds must not be greater than 999999999.',
);
}
}
}
View on GitHub (pinned to f123cdb2a2)
Solutions
- Normalize before constructing: $seconds = intdiv($nanos, 1000000000); $nanos %= 1000000000;
- Convert microseconds to nanoseconds with * 1000, milliseconds with * 1000000 only after splitting totals.
- Build aggregates with Duration::add(), which performs the >= 1000000000 carry itself.
Example fix
// before
$micros = (int) $usage['ru_utime.tv_usec'];
$d = Duration::fromSecondsAndNanoseconds(0, $micros * 1000000); // wrong factor
// after
$nanos = $micros * 1000;
$d = Duration::fromSecondsAndNanoseconds(
intdiv($nanos, 1000000000),
$nanos % 1000000000,
); Defensive patterns
Strategy: validation
Validate before calling
$nanoseconds = $microseconds * 1000; // correct us -> ns factor
$duration = Duration::fromSecondsAndNanoseconds(
intdiv($nanoseconds, 1000000000),
$nanoseconds % 1000000000,
); Type guard
function isNormalizedNanoseconds(int $nanoseconds): bool
{
return $nanoseconds >= 0 && $nanoseconds <= 999999999;
} Prevention
- Never pass raw totals into the nanoseconds slot; split with intdiv/modulo 1000000000.
- Double-check unit factors when bridging microsecond and nanosecond APIs.
- Use Duration::add() for aggregation; it carries seconds automatically.
When it happens
Trigger: Duration::fromSecondsAndNanoseconds(0, $microseconds * 1000000) (wrong factor; should be * 1000); passing an hrtime(true) difference into nanoseconds; parsing a float like 1.5 seconds into (0 seconds, 1500000000 nanoseconds).
Common situations: Custom stopwatch or reporter code bridging microsecond-precision APIs (getrusage, microtime) and nanosecond-precision values; serializing durations and reconstructing them; test data builders for telemetry fixtures.
Related errors
- Value for nanoseconds must not be greater than 999999999.
- Value for %s must not be negative.
- Value for nanoseconds must not be greater than 999999999.
- Value for %s must not be negative.
- Value for %s must not be negative.
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/6437af57aaa71ccb.
Report an issue: GitHub.