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

  1. Normalize before constructing: $seconds = intdiv($nanos, 1000000000); $nanos %= 1000000000;
  2. Convert microseconds to nanoseconds with * 1000, milliseconds with * 1000000 only after splitting totals.
  3. 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

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


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