sebastianbergmann/phpunit · error · InvalidArgumentException
Value for %s must not be negative.
Error message
Value for %s must not be negative.
What it means
CpuTime is PHPUnit's immutable value object for CPU time; its constructor runs ensureNotNegative() on both components and throws InvalidArgumentException ('Value for seconds must not be negative.' or '...nanoseconds...') for any negative input. The built-in SystemCpuTimeMeter always feeds non-negative getrusage() values, so this error comes from user code constructing CpuTime from manually computed differences that are negative.
Source
Thrown at src/Event/Value/Telemetry/CpuTime.php:110
$nanoseconds += 1000000000;
}
if ($seconds < 0) {
return new self(0, 0);
}
return new self($seconds, $nanoseconds);
}
/**
* @phpstan-assert non-negative-int $value
*
* @throws InvalidArgumentException
*/
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
- Compute differences with CpuTime::diff($other), which already normalizes the borrow and clamps negative results to zero instead of throwing.
- If doing the math yourself, clamp both components with max(0, $value) before calling fromSecondsAndNanoseconds().
- Swap the operand order so the earlier reading is subtracted from the later one.
- On 32-bit PHP, move to a 64-bit build to avoid integer wraparound in second counters.
Example fix
// before
$cpu = CpuTime::fromSecondsAndNanoseconds(
$start->seconds() - $end->seconds(), // negative when swapped
$start->nanoseconds() - $end->nanoseconds(),
);
// after
$cpu = $end->diff($start); // normalized, clamps at zero Defensive patterns
Strategy: validation
Validate before calling
$seconds = max(0, $endSeconds - $startSeconds); $nanoseconds = max(0, $endNanos - $startNanos); $cpu = CpuTime::fromSecondsAndNanoseconds($seconds, $nanoseconds); // cannot be negative now
Type guard
function isNonNegativeTimeComponent(int $value): bool
{
return $value >= 0;
} Prevention
- Use CpuTime::diff() for differences; it normalizes borrows and clamps at zero.
- Always subtract the earlier reading from the later one.
- Use 64-bit PHP for long-running processes to avoid counter wraparound.
When it happens
Trigger: CpuTime::fromSecondsAndNanoseconds($end->seconds() - $start->seconds(), ...) where the end reading predates the start (operands swapped, clocks adjusted between processes); 32-bit PHP where accumulated seconds exceed PHP_INT_MAX and wrap around negative; test fixtures with hardcoded negative values.
Common situations: Implementing a custom CpuTimeMeter for a profiler or telemetry extension; instrumenting isolated child processes where rusage counters reset; running long test suites on 32-bit builds.
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 nanoseconds must not be greater than 999999999.
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/bd4a473ecf70d1ff.
Report an issue: GitHub.