Hmbown/CodeWhale · error · PersistenceBacklogError
measurement receipt must be an object
Error message
measurement receipt must be an object
What it means
The measurement child's stdout parsed as valid JSON but the top-level value is not an object (a list, string, or number). The checker requires a single JSON object carrying the receipt fields, so array-wrapped or scalar emission is a contract violation. The emitted value's type tells you which shape change happened in the measure script.
Source
Thrown at scripts/check-persistence-backlog-budget.py:392
env["CARGO_NET_OFFLINE"] = "true"
result = subprocess.run(
[sys.executable, str(MEASURE_SCRIPT)],
cwd=ROOT,
env=env,
text=True,
capture_output=True,
check=False,
)
sys.stderr.write(result.stderr)
if result.returncode != 0:
sys.stdout.write(result.stdout)
raise PersistenceBacklogError("measurement command failed")
try:
receipt = json.loads(result.stdout)
except json.JSONDecodeError as error:
raise PersistenceBacklogError(f"measurement emitted invalid JSON: {error}") from error
if not isinstance(receipt, dict):
raise PersistenceBacklogError("measurement receipt must be an object")
return receipt
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--receipt", type=Path, help="check an existing receipt")
parser.add_argument("--budget", type=Path, default=BUDGET_PATH)
args = parser.parse_args()
try:
expected_source = current_source_identity()
receipt = load_json(args.receipt, "receipt") if args.receipt else measure()
budget = load_json(args.budget, "budget")
baseline_receipt = load_json(BASELINE_RECEIPT_PATH, "baseline receipt")
validate_baseline_receipt(budget, baseline_receipt)
increases, decreases = compare(
receipt,
budget,
expected_source=expected_source,View on GitHub (pinned to 8880682c63)
Solutions
- Check the emitted type: `python3 scripts/measure-persistence-backlog.py | python3 -c "import json,sys; print(type(json.load(sys.stdin)))"`
- Fix the script to emit exactly one receipt object (json.dumps(receipt), not a list)
- If multiple samples exist, select the canonical one before printing
- To validate a pre-existing artifact instead, pass `--receipt path.json` and bypass live measurement
Example fix
# before sys.stdout.write(json.dumps(receipts)) # list # after sys.stdout.write(json.dumps(receipts[0])) # single object
Defensive patterns
Strategy: type-guard
Validate before calling
value = json.loads(result.stdout)
assert isinstance(value, dict), f'receipt must be an object, got {type(value).__name__}' Type guard
def is_receipt_object(value):
return isinstance(value, dict) and isinstance(value.get('document_kind'), str) Prevention
- Emit exactly one JSON object from measure scripts
- Assert the top-level type in the measure script's own tests
- Use --receipt for pre-existing artifacts instead of the live measurement path
When it happens
Trigger: The measure script was changed to emit a list of per-sample receipts, a bare string/number, or a nested envelope; a different script ended up at scripts/measure-persistence-backlog.py.
Common situations: Extending the measure script for multi-sample runs without updating the checker; refactors that wrap the receipt for other consumers.
Related errors
- measurement emitted invalid JSON: {error}
- invalid {kind} {path}: top level must be an object
- measurement emitted invalid JSON: {error}
- measurement top level must be an object
- rss_during_delta_bytes is inconsistent
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/d2c3686debd9358b.
Report an issue: GitHub.