{"record":{"id":"1a36b56909a9814b","repo":"sebastianbergmann/phpunit","slug":"value-for-s-must-not-be-negative-1a36b5","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/Duration.php","lineNumber":147,"sourceCode":"            return true;\n        }\n\n        if ($this->seconds < $other->seconds) {\n            return false;\n        }\n\n        return $this->nanoseconds > $other->nanoseconds;\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":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Event/Value/Telemetry/Duration.php#L129-L165","documentation":"Duration is PHPUnit's immutable elapsed-time value object; its constructor rejects negative components with InvalidArgumentException ('Value for seconds must not be negative.' / '...nanoseconds...'). Note that HRTime::duration() and CpuTime::diff() clamp negative differences to Duration/CpuTime zero, so this error indicates manual subtraction passed to Duration::fromSecondsAndNanoseconds() rather than the built-in difference helpers.","triggerScenarios":"Duration::fromSecondsAndNanoseconds($endSeconds - $startSeconds, ...) with the end timestamp earlier than the start; monotonic-clock reads mixed with wall-clock reads across processes so the difference goes negative; integer underflow on 32-bit PHP.","commonSituations":"Extensions that report per-test durations computed from hrtime() readings captured in different processes (process isolation); building custom reporters or slow-test detectors; tests with fixture timestamps in the wrong order.","solutions":["Use the provided difference APIs, e.g. HRTime::fromSecondsAndNanoseconds(...)->duration($start), which clamps at zero.","Clamp manual results with max(0, $seconds) and max(0, $nanoseconds) before constructing.","Ensure both readings come from the same clock (always hrtime(), never mixing time() with hrtime()) and the same process where possible."],"exampleFix":"// before\n$elapsed = Duration::fromSecondsAndNanoseconds(\n    $startSec - $endSec,      // negative on swap\n    $startNanos - $endNanos,\n);\n\n// after\n$elapsed = HRTime::fromSecondsAndNanoseconds($endSec, $endNanos)\n    ->duration(HRTime::fromSecondsAndNanoseconds($startSec, $startNanos));","handlingStrategy":"validation","validationCode":"$seconds     = max(0, $endSec - $startSec);\n$nanoseconds = max(0, $endNanos - $startNanos);\n\n$duration = Duration::fromSecondsAndNanoseconds($seconds, $nanoseconds);","typeGuard":"function isNonNegativeDurationComponent(int $value): bool\n{\n    return $value >= 0;\n}","tryCatchPattern":null,"preventionTips":["Use HRTime::duration($start) instead of manual subtraction; it clamps at zero.","Take both timestamps from the same clock and process.","In reporters, treat a negative computed difference as zero elapsed time rather than constructing the object."],"tags":["phpunit","telemetry","value-object","duration","php"],"backgroundTag":"negative-duration-value","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}