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

HRTime's constructor caps nanoseconds at 999999999 (ensureNanoSecondsInRange()). Because hrtime(true) returns one total-nanoseconds integer, feeding that value (or any unsplit difference of such totals) into the nanoseconds parameter of fromSecondsAndNanoseconds() is the classic trigger.

Source

Thrown at src/Event/Value/Telemetry/HRTime.php:106

    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. Split totals before constructing: $nanos = hrtime(true) - $t0; then seconds = intdiv($nanos, 1000000000), nanoseconds = $nanos % 1000000000.
  2. Prefer hrtime(false) which already returns the [seconds, nanoseconds] pair the constructor wants.
  3. Watch unit factors: microseconds need * 1000 to become nanoseconds, not * 1000000.

Example fix

// before
$total = hrtime(true);
$stamp = HRTime::fromSecondsAndNanoseconds(0, $total); // way over 999999999

// after
[$seconds, $nanoseconds] = hrtime(false);
$stamp = HRTime::fromSecondsAndNanoseconds($seconds, $nanoseconds);
Defensive patterns

Strategy: validation

Validate before calling

[$seconds, $nanoseconds] = hrtime(false); // already a normalized pair
// or, from a total:
$total       = hrtime(true) - $t0;
$seconds     = intdiv($total, 1000000000);
$nanoseconds = $total % 1000000000;

$stamp = HRTime::fromSecondsAndNanoseconds($seconds, $nanoseconds);

Type guard

function isNormalizedNanoseconds(int $nanoseconds): bool
{
    return $nanoseconds >= 0 && $nanoseconds <= 999999999;
}

Prevention

When it happens

Trigger: HRTime::fromSecondsAndNanoseconds(0, hrtime(true)); computing a difference of two hrtime(true) totals and passing it as nanoseconds; converting a microsecond value with a 1000000 factor.

Common situations: Custom stopwatch/telemetry code switching between hrtime(true) and the (seconds, nanoseconds) pair; adapting example code written for total-nanosecond APIs; test fixtures generated from raw timestamps.

Related errors


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