langchain-ai/deepagents · error · ValueError

DiffStats counts cannot be negative, got additions=

Error message

DiffStats counts cannot be negative, got additions={self.additions}, deletions={self.deletions}

What it means

`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.

Solutions

  1. Fix the computing code so counts are clamped to >= 0 (e.g. `max(0, parsed_count)`).
  2. Validate the diff text before parsing: hunk headers must match `@@ -a,b +c,d @@` with non-negative values.
  3. Log the raw diff alongside the error to locate the malformed hunk that produced the negative number.

Example fix

// before
stats = DiffStats(additions=old_count - new_count, deletions=0)
// after
stats = DiffStats(additions=max(0, new_count - old_count), deletions=max(0, old_count - new_count))
Defensive patterns

Strategy: validation

Validate before calling

def make_diff_stats(additions: int, deletions: int) -> DiffStats:
    if additions < 0 or deletions < 0:
        raise ValueError(f"bad diff counts: +{additions} -{deletions}")
    return DiffStats(additions=additions, deletions=deletions)

Type guard

def has_valid_diff_counts(s: object) -> bool:
    return isinstance(s, DiffStats) and s.additions >= 0 and s.deletions >= 0

Try / catch

try:
    stats = DiffStats(additions=add, deletions=dele)
except ValueError as exc:
    logger.error("Diff parsing produced invalid counts: %s", exc)
    stats = DiffStats(additions=0, deletions=0)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/858c257446cfdfad. Report an issue: GitHub.

Appendix: source

Thrown at libs/code/deepagents_code/diff_utils.py:91

    additions: int
    deletions: int

    def __post_init__(self) -> None:
        """Reject negative counts.

        Both in-repo producers derive from `difflib`, so this only guards direct
        construction — but the type is public and reaches a delete prompt, where
        the number gates destroying a file.

        Raises:
            ValueError: If either count is negative.
        """
        if self.additions < 0 or self.deletions < 0:
            msg = (
                f"DiffStats counts cannot be negative, got additions="
                f"{self.additions}, deletions={self.deletions}"
            )
            raise ValueError(msg)


def split_diff_lines(diff: str) -> list[str]:
    r"""Split a unified diff back into the lines it was assembled from.

    Deliberately not `splitlines()`. Every diff reaching this function is
    `"\n"`-joined from lines that themselves came from `splitlines()` or
    `split("\n")`, so no element can contain a line boundary and `"\n"` is the
    exact inverse. `splitlines()` also breaks on `\r`, `\v`, `\f`, U+2028,
    U+2029 and U+0085, which splits a single diff line into fragments. The tail
    fragment carries no `+`/`-` marker, so it would render as an unmarked note —
    on the approval prompt that means changed content shown as neutral metadata.

    Check any new producer against that invariant rather than against a list of
    the current ones.

    Args:
        diff: Unified diff string.

View on GitHub (pinned to a1af029e6e)