{"record":{"id":"d5811f5eafd4ea60","repo":"Hmbown/CodeWhale","slug":"measurement-emitted-invalid-json-error-check-runtime","errorCode":null,"errorMessage":"measurement emitted invalid JSON: {error}","messagePattern":"measurement emitted invalid JSON: (.+?)","errorType":"exception","errorClass":"RuntimeContractError","httpStatus":null,"severity":"error","filePath":"scripts/check-runtime-contract-budget.py","lineNumber":418,"sourceCode":"    env[\"CARGO_NET_OFFLINE\"] = \"true\"\n    proc = subprocess.run(\n        [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","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/scripts/check-runtime-contract-budget.py#L400-L436","documentation":"After the measurement subprocess succeeds, run_measurement parses its stdout as JSON to obtain the receipt. If json.loads raises JSONDecodeError — the measurement printed warnings, logs, or partial output mixed with the JSON — the checker raises RuntimeContractError chaining the parse error. This isolates the contract so a malformed receipt never reaches validation.","triggerScenarios":"measure-runtime-contract.py writes anything other than pure JSON to stdout (progress prints, cargo warnings captured into stdout, a panic message after exit 0 is impossible but partial writes on crash paths), producing unparseable stdout.","commonSituations":"A modified measure script added a print() for debugging; some environment hook or wrapper emitted banner text; the JSON was truncated by an interrupted run or pipe buffering issue.","solutions":["Inspect the raw stdout shown in the JSONDecodeError context; remove stray non-JSON output (debug prints) from the measurement script.","Re-run the measurement to rule out truncation or interruption.","In measure-runtime-contract.py, send diagnostics to stderr and reserve stdout exclusively for the JSON document.","Validate the script's output manually: `python3 scripts/measure-runtime-contract.py | python3 -m json.tool`."],"exampleFix":"# before (measure script)\nprint(\"measuring...\")\nprint(json.dumps(receipt))\n# after\nprint(\"measuring...\", file=sys.stderr)\nprint(json.dumps(receipt))","handlingStrategy":"type-guard","validationCode":"import json\nraw = subprocess.run([\"python3\", \"scripts/measure-runtime-contract.py\"], capture_output=True, text=True).stdout\njson.loads(raw)  # fails fast with position info before the checker runs","typeGuard":"def is_parseable_json(text: str) -> bool:\n    import json\n    try:\n        json.loads(text)\n        return True\n    except json.JSONDecodeError:\n        return False","tryCatchPattern":"try:\n    receipt = json.loads(proc.stdout)\nexcept json.JSONDecodeError as e:\n    print(f\"non-JSON stdout at line {e.lineno} col {e.colno}: {e.msg}\", file=sys.stderr)\n    print(proc.stdout[:500], file=sys.stderr)","preventionTips":["Keep measurement stdout strictly JSON; send logs to stderr.","Pipe measurement output through python -m json.tool as a smoke test.","Avoid debug print() statements in emitters; use logging to stderr."],"tags":["json","parse","subprocess","contract-check"],"backgroundTag":"json-parse-error","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}