{"record":{"id":"7919ffb42ffb3ff0","repo":"usestrix/strix","slug":"run-json-at-path-is-unreadable-exc","errorCode":null,"errorMessage":"run.json at {path} is unreadable: {exc}","messagePattern":"run\\.json at (.+?) is unreadable: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"strix/report/writer.py","lineNumber":103,"sourceCode":"    \"\"\"Return a markdown fence tag for ``code``, defaulting to ``python`` when\n    auto-detection is inconclusive.\"\"\"\n    try:\n        lexer = guess_lexer(code)\n    except ClassNotFound:\n        return \"python\"\n    if isinstance(lexer, TextLexer) or not lexer.aliases:\n        return \"python\"\n    return str(lexer.aliases[0])\n\n\ndef read_run_record(run_dir: Path) -> dict[str, Any]:\n    path = run_record_path(run_dir)\n    if not path.exists():\n        return {}\n    try:\n        data = json.loads(path.read_text(encoding=\"utf-8\"))\n    except (OSError, json.JSONDecodeError) as exc:\n        raise RuntimeError(f\"run.json at {path} is unreadable: {exc}\") from exc\n    if not isinstance(data, dict):\n        raise TypeError(f\"run.json at {path} is not an object\")\n    return data\n\n\ndef write_run_record(run_dir: Path, run_record: dict[str, Any]) -> None:\n    _atomic_write_text(\n        run_record_path(run_dir),\n        json.dumps(run_record, ensure_ascii=False, indent=2, default=str),\n    )\n\n\ndef write_executive_report(run_dir: Path, final_scan_result: str) -> None:\n    path = run_dir / \"penetration_test_report.md\"\n    with path.open(\"w\", encoding=\"utf-8\") as f:\n        f.write(\"# Security Penetration Test Report\\n\\n\")\n        f.write(f\"**Generated:** {datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S UTC')}\\n\\n\")\n        f.write(f\"{final_scan_result}\\n\")","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/report/writer.py#L85-L121","documentation":"read_run_record() loads strix_runs/<run>/run.json to resume or report on a run. If the file exists but raises OSError or json.JSONDecodeError on read/parse, it raises RuntimeError with the underlying cause chained. Missing files are fine (returns {}); only unreadable/corrupt files fail.","triggerScenarios":"Any consumer of read_run_record (run status checks, cost accounting vs budget, report generation) over a run dir where run.json is truncated JSON or unreadable due to permissions. Distinct from a missing file, which is not an error.","commonSituations":"run.json half-written after a kill -9 or power loss (if a non-atomic write path was used); read-only or root-owned file left by a container run; file truncated by a full disk.","solutions":["Validate the file manually: python -m json.tool strix_runs/<run>/run.json to see the parse error.","Restore or regenerate run.json if the run is still recoverable; otherwise delete the run dir.","Fix permissions (chown/chmod) if OSError was the cause.","Free disk space and re-run the scan to produce a clean record."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef run_json_readable(run_dir: Path) -> bool:\n    p = run_dir / \"run.json\"\n    if not p.exists():\n        return True\n    try:\n        json.loads(p.read_text(encoding=\"utf-8\"))\n        return True\n    except (OSError, json.JSONDecodeError):\n        return False","typeGuard":null,"tryCatchPattern":"from strix.report.writer import read_run_record\ntry:\n    record = read_run_record(run_dir)\nexcept RuntimeError as exc:  # unreadable/corrupt\n    record = {}  # only if a missing record is acceptable for your flow\n    log.warning(\"skipping corrupt run record: %s\", exc)","preventionTips":["Don't hand-edit run.json; treat it as machine-managed.","Monitor disk space on the volume hosting strix_runs to avoid truncated writes.","When archiving runs, copy the whole dir atomically (rsync/tar) rather than file-by-file while a scan may still run."],"tags":["json","run-record","corruption","report"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}