Hmbown/CodeWhale · error · PersistenceBacklogError
must be a JSON object
Error message
{label} must be a JSON object What it means
After successfully parsing a labeled file, load_json additionally requires the top-level value to be a JSON object (dict). Arrays, strings, numbers, or null at the top level raise PersistenceBacklogError, because the persistence-backlog budget checker needs named fields from the receipt and budget documents.
Solutions
- Inspect the file's top level and wrap the content in the expected object shape
- Regenerate the receipt/budget with the current producing tool so it emits a JSON object
- Update the writer to serialize an object with the fields the checker expects
- If the checker must accept a new shape, extend load_json rather than editing data ad hoc
Example fix
// before
[1, 2, 3]
// after
{"entries": [1, 2, 3]} Defensive patterns
Strategy: type-guard
Validate before calling
import json
doc = json.loads(Path("receipt.json").read_text())
assert isinstance(doc, dict), "receipt must be a JSON object at top level" Type guard
def is_json_object(value: object) -> bool:
return isinstance(value, dict)
if not is_json_object(doc):
raise PersistenceBacklogError("receipt must be a JSON object") Try / catch
try:
doc = load_json(path, "receipt")
except PersistenceBacklogError as exc:
print(f"bad receipt shape: {exc}"); sys.exit(1) Prevention
- Keep the writer and checker schemas in sync; change both in the same PR
- Never replace the object root with an array when flattening manually
- Snapshot a known-good receipt and diff against it after pipeline changes
- Add a smoke test that loads the current receipt through load_json
When it happens
Trigger: A receipt or budget file whose root is a JSON array or scalar — e.g. the producer exported a list of entries instead of an object keyed by field name, or the file was hand-edited/replaced with a differently shaped document.
Common situations: Schema drift between the tool that writes the receipt and this checker; someone flattening the JSON during a manual edit; a pipeline step emitting an array of per-day records where the checker expects one object.
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
- receipt document_kind must be
- receipt missing required field(s)
- document_kind must be ` `, got
- Burn rate is optional. When set, it must be a positive $/hr.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/e5b2661c8b11c818.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/check-persistence-backlog-budget.py:92
"rss_after_delta_bytes",
)
RSS_SAMPLE_FIELDS = ("rss_before_bytes", "rss_during_bytes", "rss_after_bytes")
RSS_DELTA_FIELDS = ("rss_during_delta_bytes", "rss_after_delta_bytes")
SUPPORTED_PLATFORMS = {"linux", "macos", "windows"}
SOURCE_SHA_PATTERN = re.compile(r"[0-9a-f]{40}")
class PersistenceBacklogError(ValueError):
"""A receipt or budget broke the measurement contract."""
def load_json(path: Path, label: str) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise PersistenceBacklogError(f"invalid {label} {path}: {error}") from error
if not isinstance(value, dict):
raise PersistenceBacklogError(f"{label} must be a JSON object")
return value
def non_negative_integer(value: Any, field: str) -> int:
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
raise PersistenceBacklogError(f"{field} must be a non-negative integer")
return value
def validate_frozen_field(field: str, value: Any, expected: Any) -> None:
if type(value) is not type(expected) or value != expected:
raise PersistenceBacklogError(
f"receipt {field} must remain {expected!r}, got {value!r}"
)
def current_source_identity() -> dict[str, Any]:
def run(command: list[str]) -> str:View on GitHub (pinned to 433685b202)