Hmbown/CodeWhale · error · PersistenceBacklogError

{field} must be a non-negative integer

Error message

{field} must be a non-negative integer

What it means

non_negative_integer is the checker's field validator for counts in the budget and receipt documents. It rejects a value that is a bool (bools are ints in Python but explicitly disallowed), not an int (floats and strings fail), or negative. The message interpolates the offending field name so you know exactly which key broke the contract.

Source

Thrown at scripts/check-persistence-backlog-budget.py:98


class PersistenceBacklogError(ValueError):
    """A receipt or budget broke the measurement contract."""


def load_json(path: Path, label: str) -> dict[str, Any]:
    try:
        value = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as error:
        raise PersistenceBacklogError(f"invalid {label} {path}: {error}") from error
    if not isinstance(value, dict):
        raise PersistenceBacklogError(f"{label} must be a JSON object")
    return value


def non_negative_integer(value: Any, field: str) -> int:
    if isinstance(value, bool) or not isinstance(value, int) or value < 0:
        raise PersistenceBacklogError(f"{field} must be a non-negative integer")
    return value


def validate_frozen_field(field: str, value: Any, expected: Any) -> None:
    if type(value) is not type(expected) or value != expected:
        raise PersistenceBacklogError(
            f"receipt {field} must remain {expected!r}, got {value!r}"
        )


def current_source_identity() -> dict[str, Any]:
    def run(command: list[str]) -> str:
        result = subprocess.run(
            command,
            cwd=ROOT,
            text=True,
            capture_output=True,
            check=False,

View on GitHub (pinned to 8880682c63)

Solutions

  1. Find the named field in the budget/receipt JSON and set it to a whole number >= 0.
  2. If the value comes from a generator script, coerce with int(round(x)) at write time and assert non-negative before serializing.
  3. Remove booleans — numeric contract fields take 0/1, not true/false.
  4. Re-run the checker; field-level errors are reported one at a time, so iterate until the document validates.

Example fix

# before
{"max_files": 5000.0, "max_retries": -1}
# after
{"max_files": 5000, "max_retries": 0}
Defensive patterns

Strategy: validation

Validate before calling

def is_non_negative_integer(value: object) -> bool:
    return isinstance(value, int) and not isinstance(value, bool) and value >= 0

Type guard

def is_count_field(value: object) -> bool:
    # accepts only true ints in [0, 2**63-1]; rejects bools, floats, strings
    return (
        isinstance(value, int)
        and not isinstance(value, bool)
        and 0 <= value <= 2**63 - 1
    )

Prevention

When it happens

Trigger: A budget field like "max_files": -1 or 5000.0; a receipt count serialized as a string ("3") by another language's JSON writer; a boolean true/false left in place of a numeric flag. All raise before any comparison against the baseline runs.

Common situations: Hand-editing the budget and using a float or negative sentinel; receipts generated by measurement tooling in JS/Go that emits strings or floats for counts; JSON serializers that write 0.0 or 1e3; someone using true as shorthand for 1.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/1d89c4e36083a136. Report an issue: GitHub.