{"record":{"id":"74abeca03f99212a","repo":"Hmbown/CodeWhale","slug":"measurement-top-level-must-be-an-object","errorCode":null,"errorMessage":"measurement top level must be an object","messagePattern":"measurement top level must be an object","errorType":"exception","errorClass":"RuntimeContractError","httpStatus":null,"severity":"error","filePath":"scripts/check-runtime-contract-budget.py","lineNumber":413,"sourceCode":"        [sys.executable, str(MEASURE_SCRIPT)],\n        cwd=REPO_ROOT,\n        env=env,\n        capture_output=True,\n        text=True,\n        check=False,\n    )\n    sys.stderr.write(proc.stderr)\n    if proc.returncode != 0:\n        sys.stdout.write(proc.stdout)\n        raise RuntimeContractError(\n            f\"runtime-contract measurement failed with exit code {proc.returncode}\"\n        )\n    try:\n        receipt = json.loads(proc.stdout)\n    except json.JSONDecodeError as error:\n        raise RuntimeContractError(f\"measurement emitted invalid JSON: {error}\") from error\n    if not isinstance(receipt, dict):\n        raise RuntimeContractError(\"measurement top level must be an object\")\n    validate_receipt(receipt)\n    return receipt\n\n\ndef update_command(receipt_path: Path | None, budget_path: Path) -> str:\n    parts = [\"python3\", \"scripts/check-runtime-contract-budget.py\"]\n    if receipt_path is not None:\n        parts.extend([\"--receipt\", str(receipt_path)])\n    if budget_path != BUDGET_PATH:\n        parts.extend([\"--budget\", str(budget_path)])\n    parts.append(\"--update\")\n    return shlex.join(parts)\n\n\nFRAGMENT_MODULE = REPO_ROOT / \"crates\" / \"core\" / \"src\" / \"fragments.rs\"\nFRAGMENT_MAX_TOKENS_CEILING = 10_000\nFRAGMENT_MAX_BYTES_CEILING = FRAGMENT_MAX_TOKENS_CEILING * 4\nFRAGMENT_DEFAULT_MAX_BYTES_CEILING = 4 * 1024","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/scripts/check-runtime-contract-budget.py#L395-L431","documentation":"The measurement stdout parsed as valid JSON, but the top-level value is not an object (dict) - for example an array, a bare string, or a number. run_measurement() requires a top-level object because validate_receipt() navigates keys like document_kind and tool_catalog. Distinct from error 211, which covers stdout that is not JSON at all.","triggerScenarios":"A measure script change that emits a list of receipts or a JSON scalar; a wrapper that outputs a JSON array of log lines; hand-fabricated measurement output for testing the checker.","commonSituations":"Refactoring measure-runtime-contract.py's serialization and accidentally wrapping the receipt in a list; piping through jq without the leading '.' selection; test harnesses that emit NDJSON (one object per line), which parses as error 211 unless wrapped in [...], and then fails here.","solutions":["Make the measure script print exactly one top-level JSON object and nothing else on stdout","If you need multiple documents, wrap them in an object keyed by purpose rather than a top-level array","Check the emitted shape first: python3 -c \"import json,sys;print(type(json.load(sys.stdin)))\" < out.json"],"exampleFix":"# before (scripts/measure-runtime-contract.py)\nprint(json.dumps([receipt]))\n\n# after\nprint(json.dumps(receipt))","handlingStrategy":"type-guard","validationCode":"import json\n\n\ndef measurement_emits_object() -> bool:\n    proc = subprocess.run(\n        [sys.executable, \"scripts/measure-runtime-contract.py\"],\n        capture_output=True,\n        text=True,\n    )\n    if proc.returncode != 0:\n        return False\n    try:\n        return isinstance(json.loads(proc.stdout), dict)\n    except json.JSONDecodeError:\n        return False","typeGuard":"import json\n\ndef parses_to_json_object(stdout: str) -> bool:\n    try:\n        return isinstance(json.loads(stdout), dict)\n    except json.JSONDecodeError:\n        return False","tryCatchPattern":null,"preventionTips":["Emit exactly one JSON object from the measure script and nothing else on stdout","Do not wrap receipts in arrays or emit NDJSON on the measurement pipe","When adding fields to the receipt, keep them inside the single top-level object and re-run the checker with --receipt"],"tags":["python","json","subprocess","typing"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}