Hmbown/CodeWhale · error · RuntimeContractError
{kind} metric `{dotted}` must be a non-negative integer
Error message
{kind} metric `{dotted}` must be a non-negative integer What it means
metric_value() enforces that every METRICS path (system-prompt bytes/tokens/blocks per mode, stage bytes and deltas, tool counts and schema bytes/tokens, skill-discovery deltas) is a non-negative integer, with bool explicitly rejected because bool is a subclass of int in Python. It runs inside compare() for both receipt and budget documents, and inside budget_from_receipt(). The message names the exact dotted path.
Source
Thrown at scripts/check-runtime-contract-budget.py:299
representative = budget.get("representative_context")
fixture_id = (
representative.get("fixture_id")
if isinstance(representative, dict)
else None
)
if fixture_id != REPRESENTATIVE_FIXTURE_ID:
raise RuntimeContractError(
"budget metric `representative_context.fixture_id` must be "
f"`{REPRESENTATIVE_FIXTURE_ID}`, got {fixture_id!r}"
)
validate_identity_structure(budget, "budget")
def metric_value(document: dict[str, Any], path: MetricPath, kind: str) -> int:
value = required_value(document, path, kind)
dotted = ".".join(path)
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
raise RuntimeContractError(
f"{kind} metric `{dotted}` must be a non-negative integer"
)
return value
def compare(
receipt: dict[str, Any], budget: dict[str, Any]
) -> tuple[list[MetricResult], list[MetricResult]]:
"""Return (increases, decreases) as path/label/current/ceiling tuples."""
validate_receipt(receipt)
validate_budget(budget)
for path, label in IDENTITIES:
receipt_value = required_value(receipt, path, "receipt")
budget_value = required_value(budget, path, "budget")
if receipt_value != budget_value:
detail = ""
if isinstance(receipt_value, list) and isinstance(budget_value, list):
added = [str(item) for item in receipt_value if item not in budget_value]View on GitHub (pinned to 8880682c63)
Solutions
- Rewrite the named metric as a plain non-negative JSON integer (no quotes, no decimal point, no sign)
- Sweep the whole file for the same defect: python3 -c "import json;d=json.load(open('scripts/runtime-contract-budget.json'));print([k for k,v in walk(d) if isinstance(v,float)])" style scan
- If the measure script is emitting floats, fix its serialization to int before re-measuring
Example fix
// before "total_tokens_est": 2149.0 "system_prompt_bytes": "6084" // after "total_tokens_est": 2149 "system_prompt_bytes": 6084
Defensive patterns
Strategy: type-guard
Validate before calling
def all_metrics_are_ints(doc: dict) -> bool:
stack = [doc]
while stack:
node = stack.pop()
if isinstance(node, dict):
stack.extend(node.values())
elif isinstance(node, list):
stack.extend(node)
elif isinstance(node, float) or isinstance(node, bool):
return False
return True Type guard
def is_non_negative_int(value: object) -> bool:
return isinstance(value, int) and not isinstance(value, bool) and value >= 0 Prevention
- Never store metrics as strings or floats in the budget JSON, even transiently
- Migrate metrics with a script that coerces via int(round(x)) rather than by hand
- Remember bool is an int subclass in Python - the checker deliberately rejects true/false metrics
When it happens
Trigger: A metric stored as a float (2149.0), a numeric string ("6084"), a negative number, or true/false - typically after hand-editing the budget JSON. Also a measure-script change that starts emitting float token estimates.
Common situations: Hand-tightening a ceiling in the budget and typing a decimal or string; JSON round-trips through tooling that coerces ints to floats (some YAML/JS pipelines); a receipt edited in a JSON editor that quotes numbers.
Related errors
- budget metric `representative_context.fixture_id` must be `{
- baseline provenance build profile/sample count changed
- invalid {kind} {path}: {error}
- invalid {kind} {path}: top level must be an object
- {source} document_kind must be `{expected_kind}`, got {actua
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/8ac95eb70e134b6e.
Report an issue: GitHub.