{"record":{"id":"bd4a473ecf70d1ff","repo":"sebastianbergmann/phpunit","slug":"value-for-s-must-not-be-negative","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/CpuTime.php","lineNumber":110,"sourceCode":"            $nanoseconds += 1000000000;\n        }\n\n        if ($seconds < 0) {\n            return new self(0, 0);\n        }\n\n        return new self($seconds, $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":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Event/Value/Telemetry/CpuTime.php#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Compute differences with CpuTime::diff($other), which already normalizes the borrow and clamps negative results to zero instead of throwing.","If doing the math yourself, clamp both components with max(0, $value) before calling fromSecondsAndNanoseconds().","Swap the operand order so the earlier reading is subtracted from the later one.","On 32-bit PHP, move to a 64-bit build to avoid integer wraparound in second counters."],"exampleFix":"// before\n$cpu = CpuTime::fromSecondsAndNanoseconds(\n    $start->seconds() - $end->seconds(),   // negative when swapped\n    $start->nanoseconds() - $end->nanoseconds(),\n);\n\n// after\n$cpu = $end->diff($start); // normalized, clamps at zero","handlingStrategy":"validation","validationCode":"$seconds     = max(0, $endSeconds - $startSeconds);\n$nanoseconds = max(0, $endNanos - $startNanos);\n\n$cpu = CpuTime::fromSecondsAndNanoseconds($seconds, $nanoseconds); // cannot be negative now","typeGuard":"function isNonNegativeTimeComponent(int $value): bool\n{\n    return $value >= 0;\n}","tryCatchPattern":null,"preventionTips":["Use CpuTime::diff() for differences; it normalizes borrows and clamps at zero.","Always subtract the earlier reading from the later one.","Use 64-bit PHP for long-running processes to avoid counter wraparound."],"tags":["phpunit","telemetry","value-object","cpu-time","php"],"backgroundTag":"negative-duration-value","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}