Hmbown/CodeWhale · error · RuntimeContractError
document_kind must be ` `, got
Error message
{source} document_kind must be `{expected_kind}`, got {actual_kind!r} What it means
validate_document checks that a parsed receipt/budget JSON has the required `document_kind` discriminator equal to the expected kind (codewhale.runtime_contract_receipt or codewhale.runtime_contract_budget). A mismatch or missing key raises this RuntimeContractError, preventing a receipt from being validated as a budget or vice versa.
Solutions
- Check the `got` value in the message: pass the receipt file to --receipt and keep scripts/runtime-contract-budget.json as the budget input.
- Set document_kind to the expected string ("codewhale.runtime_contract_receipt" or "codewhale.runtime_contract_budget") if the file's kind was renamed.
- Regenerate the document with the current tool version if it predates the schema rename.
Example fix
// before (receipt.json)
{"document_kind": "codewhale.runtime_contract_budget", ...}
// after
{"document_kind": "codewhale.runtime_contract_receipt", ...} Defensive patterns
Strategy: validation
Validate before calling
import json
from pathlib import Path
doc = json.loads(Path(path).read_text(encoding="utf-8"))
expected = "codewhale.runtime_contract_receipt"
if doc.get("document_kind") != expected:
raise SystemExit(f"{path}: document_kind must be {expected!r}") Type guard
def has_document_kind(doc, kind: str) -> bool:
return isinstance(doc, dict) and doc.get("document_kind") == kind Try / catch
try:
validate_document(doc, RECEIPT_KIND, "receipt")
except RuntimeContractError as e:
print(e); sys.exit(1) # message shows expected vs actual kind Prevention
- Never swap the receipt and budget files; keep their names distinct (receipt.json vs runtime-contract-budget.json).
- Include document_kind in every generated contract document and assert it at write time.
- If renaming the discriminator, bump schema_version and migrate old files.
When it happens
Trigger: Passing a budget JSON via --receipt (or the receipt path to the budget loader); a document missing document_kind entirely; an old or differently-named discriminator field.
Common situations: Swapping the two file arguments; pointing the checker at an unrelated JSON config; a schema rename between tool versions left an old file with a stale document_kind.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- invalid : top level must be an object
- Cargo metadata must contain workspace_members and packages…
- Cargo metadata root must be an object
- must be a JSON object
- measurement receipt must be an object
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/4f6d251104112914.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/check-runtime-contract-budget.py:177
def load_json(path: Path, kind: str) -> dict[str, Any]:
try:
document = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError as error:
raise RuntimeContractError(f"missing {kind}: {path}") from error
except (OSError, json.JSONDecodeError) as error:
raise RuntimeContractError(f"invalid {kind} {path}: {error}") from error
if not isinstance(document, dict):
raise RuntimeContractError(f"invalid {kind} {path}: top level must be an object")
return document
def validate_document(
document: dict[str, Any], expected_kind: str, source: str
) -> None:
actual_kind = document.get("document_kind")
if actual_kind != expected_kind:
raise RuntimeContractError(
f"{source} document_kind must be `{expected_kind}`, got {actual_kind!r}"
)
version = document.get("schema_version")
if (
isinstance(version, bool)
or not isinstance(version, int)
or version != SCHEMA_VERSION
):
raise RuntimeContractError(
f"{source} schema_version must be {SCHEMA_VERSION}, got {version!r}"
)
def required_value(document: dict[str, Any], path: MetricPath, kind: str) -> Any:
value: Any = document
dotted = ".".join(path)
for part in path:
if not isinstance(value, dict) or part not in value:View on GitHub (pinned to 433685b202)