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

  1. Compute differences with CpuTime::diff($other), which already normalizes the borrow and clamps negative results to zero instead of throwing.
  2. If doing the math yourself, clamp both components with max(0, $value) before calling fromSecondsAndNanoseconds().
  3. Swap the operand order so the earlier reading is subtracted from the later one.
  4. 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

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


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/bd4a473ecf70d1ff. Report an issue: GitHub.