{"record":{"id":"858c257446cfdfad","repo":"langchain-ai/deepagents","slug":"diffstats-counts-cannot-be-negative-got-additions","errorCode":null,"errorMessage":"DiffStats counts cannot be negative, got additions={self.additions}, deletions={self.deletions}","messagePattern":"DiffStats counts cannot be negative, got additions=(.+?), deletions=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/diff_utils.py","lineNumber":91,"sourceCode":"    additions: int\n    deletions: int\n\n    def __post_init__(self) -> None:\n        \"\"\"Reject negative counts.\n\n        Both in-repo producers derive from `difflib`, so this only guards direct\n        construction — but the type is public and reaches a delete prompt, where\n        the number gates destroying a file.\n\n        Raises:\n            ValueError: If either count is negative.\n        \"\"\"\n        if self.additions < 0 or self.deletions < 0:\n            msg = (\n                f\"DiffStats counts cannot be negative, got additions=\"\n                f\"{self.additions}, deletions={self.deletions}\"\n            )\n            raise ValueError(msg)\n\n\ndef split_diff_lines(diff: str) -> list[str]:\n    r\"\"\"Split a unified diff back into the lines it was assembled from.\n\n    Deliberately not `splitlines()`. Every diff reaching this function is\n    `\"\\n\"`-joined from lines that themselves came from `splitlines()` or\n    `split(\"\\n\")`, so no element can contain a line boundary and `\"\\n\"` is the\n    exact inverse. `splitlines()` also breaks on `\\r`, `\\v`, `\\f`, U+2028,\n    U+2029 and U+0085, which splits a single diff line into fragments. The tail\n    fragment carries no `+`/`-` marker, so it would render as an unmarked note —\n    on the approval prompt that means changed content shown as neutral metadata.\n\n    Check any new producer against that invariant rather than against a list of\n    the current ones.\n\n    Args:\n        diff: Unified diff string.","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/diff_utils.py#L73-L109","documentation":"`DiffStats.__post_init__` validates that `additions` and `deletions` are non-negative. A negative count means the caller computed diff statistics incorrectly (e.g. mis-parsed hunk headers like `@@ -1,3 +1,-2 @@` or subtracted counts in the wrong order). The library throws immediately rather than propagating nonsense metrics into UI or reporting code.","triggerScenarios":"Constructing `DiffStats(additions=..., deletions=...)` with either field negative — usually counts derived from malformed diff hunk headers or by subtracting a larger value from a smaller one.","commonSituations":"Parsing truncated or hand-edited unified diffs; a hunk-header regex capturing a stray minus sign; off-by-one arithmetic when computing net changes; feeding a custom diff format the parser was not designed for.","solutions":["Fix the computing code so counts are clamped to >= 0 (e.g. `max(0, parsed_count)`).","Validate the diff text before parsing: hunk headers must match `@@ -a,b +c,d @@` with non-negative values.","Log the raw diff alongside the error to locate the malformed hunk that produced the negative number."],"exampleFix":"// before\nstats = DiffStats(additions=old_count - new_count, deletions=0)\n// after\nstats = DiffStats(additions=max(0, new_count - old_count), deletions=max(0, old_count - new_count))","handlingStrategy":"validation","validationCode":"def make_diff_stats(additions: int, deletions: int) -> DiffStats:\n    if additions < 0 or deletions < 0:\n        raise ValueError(f\"bad diff counts: +{additions} -{deletions}\")\n    return DiffStats(additions=additions, deletions=deletions)","typeGuard":"def has_valid_diff_counts(s: object) -> bool:\n    return isinstance(s, DiffStats) and s.additions >= 0 and s.deletions >= 0","tryCatchPattern":"try:\n    stats = DiffStats(additions=add, deletions=dele)\nexcept ValueError as exc:\n    logger.error(\"Diff parsing produced invalid counts: %s\", exc)\n    stats = DiffStats(additions=0, deletions=0)","preventionTips":["Clamp parsed hunk-header values with max(0, value).","Test your diff parser against truncated and hand-edited diffs.","Parse additions and deletions independently; never derive one by subtracting the other."],"tags":["diff","validation","dataclass","input-error"],"backgroundTag":"negative-count-validation","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}