{"record":{"id":"e5b2661c8b11c818","repo":"Hmbown/CodeWhale","slug":"label-must-be-a-json-object","errorCode":null,"errorMessage":"{label} must be a JSON object","messagePattern":"(.+?) must be a JSON object","errorType":"exception","errorClass":"PersistenceBacklogError","httpStatus":null,"severity":"error","filePath":"scripts/check-persistence-backlog-budget.py","lineNumber":92,"sourceCode":"    \"rss_after_delta_bytes\",\n)\nRSS_SAMPLE_FIELDS = (\"rss_before_bytes\", \"rss_during_bytes\", \"rss_after_bytes\")\nRSS_DELTA_FIELDS = (\"rss_during_delta_bytes\", \"rss_after_delta_bytes\")\nSUPPORTED_PLATFORMS = {\"linux\", \"macos\", \"windows\"}\nSOURCE_SHA_PATTERN = re.compile(r\"[0-9a-f]{40}\")\n\n\nclass PersistenceBacklogError(ValueError):\n    \"\"\"A receipt or budget broke the measurement contract.\"\"\"\n\n\ndef load_json(path: Path, label: str) -> dict[str, Any]:\n    try:\n        value = json.loads(path.read_text(encoding=\"utf-8\"))\n    except (OSError, json.JSONDecodeError) as error:\n        raise PersistenceBacklogError(f\"invalid {label} {path}: {error}\") from error\n    if not isinstance(value, dict):\n        raise PersistenceBacklogError(f\"{label} must be a JSON object\")\n    return value\n\n\ndef non_negative_integer(value: Any, field: str) -> int:\n    if isinstance(value, bool) or not isinstance(value, int) or value < 0:\n        raise PersistenceBacklogError(f\"{field} must be a non-negative integer\")\n    return value\n\n\ndef validate_frozen_field(field: str, value: Any, expected: Any) -> None:\n    if type(value) is not type(expected) or value != expected:\n        raise PersistenceBacklogError(\n            f\"receipt {field} must remain {expected!r}, got {value!r}\"\n        )\n\n\ndef current_source_identity() -> dict[str, Any]:\n    def run(command: list[str]) -> str:","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/scripts/check-persistence-backlog-budget.py#L74-L110","documentation":"After successfully parsing the budget or receipt JSON text, load_json requires the top-level value to be a JSON object (dict), not an array, string, number, or null. This error names which label ('budget'/'baseline receipt') failed the shape check before any field validation runs.","triggerScenarios":"The budget file contains a JSON array of entries (e.g. '[{\"max_files\": ...}]'), a bare string/number, or 'null', all of which parse fine but are not objects. Only a {\"...\": ...} document at the top level is accepted.","commonSituations":"Hand-writing the budget as a list of rules; tools that emit NDJSON or a top-level array; an empty file left as 'null' by a serializer; refactoring the file format without updating the checker's expectation.","solutions":["Open the named file and wrap the top-level value in an object: {\"entries\": [...]} or the documented field names.","Compare against the checked-in format used by the existing budget/receipt files in scripts/.","If the array shape is the intended new format, update load_json and the downstream validators in the same change with tests.","Re-run scripts/check-persistence-backlog-budget.py to confirm field-level validation proceeds."],"exampleFix":"# before: scripts/persistence-backlog-budget.json\n[{\"max_files\": 5000, \"max_bytes\": 1048576}]\n# after\n{\"max_files\": 5000, \"max_bytes\": 1048576}","handlingStrategy":"type-guard","validationCode":"import json\n\ndef top_level_is_object(path) -> bool:\n    return isinstance(json.loads(path.read_text(encoding=\"utf-8\")), dict)","typeGuard":"def is_json_object(value: object) -> bool:\n    return isinstance(value, dict)","tryCatchPattern":null,"preventionTips":["Keep the budget/receipt top-level shape an object; nest arrays under named keys.","Copy the checked-in file's structure when adding entries instead of inventing a new shape.","Add a schema smoke-test in CI that asserts isinstance(loaded, dict) before the full check runs."],"tags":["json","validation","persistence-budget","ci"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}