{"record":{"id":"faeb5dd87f14d587","repo":"sebastianbergmann/phpunit","slug":"value-for-s-must-not-be-negative-faeb5d","errorCode":null,"errorMessage":"Value for %s must not be negative.","messagePattern":"Value for (.+?) must not be negative\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Event/Value/Telemetry/HRTime.php","lineNumber":91,"sourceCode":"        if ($seconds < 0) {\n            return Duration::fromSecondsAndNanoseconds(0, 0);\n        }\n\n        return Duration::fromSecondsAndNanoseconds(\n            $seconds,\n            $nanoseconds,\n        );\n    }\n\n    /**\n     * @phpstan-assert non-negative-int $value\n     *\n     * @throws InvalidArgumentException\n     */\n    private function ensureNotNegative(int $value, string $type): void\n    {\n        if ($value < 0) {\n            throw new InvalidArgumentException(\n                sprintf(\n                    'Value for %s must not be negative.',\n                    $type,\n                ),\n            );\n        }\n    }\n\n    /**\n     * @throws InvalidArgumentException\n     */\n    private function ensureNanoSecondsInRange(int $nanoseconds): void\n    {\n        if ($nanoseconds > 999999999) {\n            throw new InvalidArgumentException(\n                'Value for nanoseconds must not be greater than 999999999.',\n            );\n        }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Event/Value/Telemetry/HRTime.php#L73-L109","documentation":"HRTime is PHPUnit's immutable high-resolution timestamp value object (built on hrtime()); its constructor rejects negative seconds or nanoseconds. The Facade uses it for stopwatch starts and for SystemStopWatchWithOffset in process isolation, so the error typically comes from user code computing a negative difference or offset and feeding it to HRTime::fromSecondsAndNanoseconds().","triggerScenarios":"HRTime::fromSecondsAndNanoseconds($t1 - $t0, ...) where $t1 < $t0 (swapped hrtime readings); computing an offset for a child process whose clock reading is behind the parent's; casting a negative float from hrtime(false) arithmetic to int.","commonSituations":"Extensions capturing their own hrtime() anchors around tests that run in isolated processes; custom stopwatch implementations; 32-bit platforms where nanosecond totals overflow.","solutions":["Order the operands: subtract the earlier reading from the later one and verify with $later >= $earlier before constructing.","Use HRTime::duration($start) for differences; it borrows across the second boundary and clamps at zero instead of throwing.","Keep seconds and nanoseconds as the split hrtime(false) pair (or split hrtime(true) totals with intdiv) rather than mixing clock types."],"exampleFix":"// before\n$stamp = HRTime::fromSecondsAndNanoseconds($t0Sec - $t1Sec, $t0Nanos - $t1Nanos); // may be negative\n\n// after\n$stamp = HRTime::fromSecondsAndNanoseconds($t1Sec, $t1Nanos);\n$elapsed = $stamp->duration(HRTime::fromSecondsAndNanoseconds($t0Sec, $t0Nanos));","handlingStrategy":"validation","validationCode":"[$seconds, $nanoseconds] = hrtime(false); // both non-negative by contract\n\nif ($seconds < 0 || $nanoseconds < 0) {\n    throw new LogicException('hrtime() returned unexpected negative values');\n}\n\n$stamp = HRTime::fromSecondsAndNanoseconds($seconds, $nanoseconds);","typeGuard":"function isNonNegativeTimeComponent(int $value): bool\n{\n    return $value >= 0;\n}","tryCatchPattern":null,"preventionTips":["Use HRTime::duration($start) for differences instead of manual subtraction.","Anchor timestamps with hrtime(false) so the (seconds, nanoseconds) split is native.","Validate that child-process offsets are computed from readings of the same monotonic clock."],"tags":["phpunit","telemetry","value-object","hrtime","php"],"backgroundTag":"negative-duration-value","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}