Hmbown/CodeWhale · error · RuntimeContractError

invalid : top level must be an object

Error message

invalid {kind} {path}: top level must be an object

What it means

After successfully parsing the JSON, load_json requires the top-level value to be a JSON object (dict). Arrays, strings, numbers, or null at the top level raise this RuntimeContractError, because receipt and budget documents are always objects with document_kind/schema_version fields.

Solutions

  1. Open the file and confirm the top level is `{...}`; wrap the value in an object if the producer wrote an array or scalar.
  2. Fix the producer so it emits a single receipt/budget object with document_kind and schema_version fields.
  3. Point --receipt at the correct per-run receipt file rather than an aggregate list.

Example fix

// before (receipt.json)
[{"document_kind": "codewhale.runtime_contract_receipt"}]
// after
{"document_kind": "codewhale.runtime_contract_receipt", "schema_version": 1}
Defensive patterns

Strategy: type-guard

Validate before calling

import json
from pathlib import Path
doc = json.loads(Path(path).read_text(encoding="utf-8"))
if not isinstance(doc, dict):
    raise SystemExit(f"{path}: top level must be a JSON object")

Type guard

def is_receipt_object(path) -> bool:
    import json
    from pathlib import Path
    try:
        return isinstance(json.loads(Path(path).read_text(encoding="utf-8")), dict)
    except Exception:
        return False

Try / catch

try:
    doc = load_json(path, "receipt")
except RuntimeContractError as e:
    if "top level must be an object" in str(e):
        print(f"{path} holds a non-object JSON value; fix the producer")
    raise

Prevention

When it happens

Trigger: Passing a --receipt file whose content is a JSON array, string, number, boolean, or null — e.g. a file holding `[1,2,3]`, a list of receipts, or `null` from a failed serialization.

Common situations: A tool wrote a JSON array of results instead of a single object; a placeholder or test fixture file was pointed at; a script serialized the wrong variable.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/c2609820ae61b57a. Report an issue: GitHub.

Appendix: source

Thrown at scripts/check-runtime-contract-budget.py:168

        )
        for stage, label in REPRESENTATIVE_STAGES
    ),
)


class RuntimeContractError(ValueError):
    """A receipt or budget is missing a required, well-typed metric."""


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(

View on GitHub (pinned to 433685b202)