usestrix/strix · error · TypeError
run.json at {path} is not an object
Error message
run.json at {path} is not an object What it means
After read_run_record() parses run.json, it enforces that the top-level value is a JSON object (dict) before returning it; anything else raises TypeError. Callers rely on dict access (data.get(...)) for status, llm_usage.cost, etc., so a non-object file is a hard schema violation.
Source
Thrown at strix/report/writer.py:105
try:
lexer = guess_lexer(code)
except ClassNotFound:
return "python"
if isinstance(lexer, TextLexer) or not lexer.aliases:
return "python"
return str(lexer.aliases[0])
def read_run_record(run_dir: Path) -> dict[str, Any]:
path = run_record_path(run_dir)
if not path.exists():
return {}
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise RuntimeError(f"run.json at {path} is unreadable: {exc}") from exc
if not isinstance(data, dict):
raise TypeError(f"run.json at {path} is not an object")
return data
def write_run_record(run_dir: Path, run_record: dict[str, Any]) -> None:
_atomic_write_text(
run_record_path(run_dir),
json.dumps(run_record, ensure_ascii=False, indent=2, default=str),
)
def write_executive_report(run_dir: Path, final_scan_result: str) -> None:
path = run_dir / "penetration_test_report.md"
with path.open("w", encoding="utf-8") as f:
f.write("# Security Penetration Test Report\n\n")
f.write(f"**Generated:** {datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S UTC')}\n\n")
f.write(f"{final_scan_result}\n")
logger.info("Saved final penetration test report to: %s", path)
View on GitHub (pinned to 8551339130)
Solutions
- Inspect run.json and rewrite it as a single JSON object with the expected keys (status, llm_usage, scan_results, ...).
- If unrecoverable, delete the run dir or regenerate the record with a new scan.
- Audit any script that writes into strix_runs/<run>/ and keep run.json write-protected from ad-ho tooling.
Example fix
// before: run.json
["status", "completed"]
// after: run.json
{"status": "completed", "llm_usage": {"cost": 0.0}} Defensive patterns
Strategy: type-guard
Validate before calling
import json
from pathlib import Path
def run_json_is_object(run_dir: Path) -> bool:
p = run_dir / "run.json"
if not p.exists():
return True
try:
return isinstance(json.loads(p.read_text(encoding="utf-8")), dict)
except (OSError, json.JSONDecodeError):
return False Type guard
def is_run_record(data: object) -> bool:
return isinstance(data, dict) Try / catch
from strix.report.writer import read_run_record
try:
record = read_run_record(run_dir)
except TypeError:
# schema violation: run.json top level must be an object
raise Prevention
- Never redirect command output into run.json (`cmd > run.json` is a classic footgun).
- Script read access via read_run_record() instead of ad-hoc json.load so you get typed errors.
- Keep third-party tools from writing into strix_runs/<run>/.
When it happens
Trigger: run.json contains a JSON array, string, or number at top level. Typically caused by manual editing, a redirected output overwriting the file (e.g. `cmd > run.json` writing non-JSON), or a foreign tool writing into the run dir.
Common situations: Operators scripting over run.json and accidentally replacing it; log redirection mistakes; artifacts produced by a different/older schema.
Related errors
- vulnerabilities.json at {path} is not a list
- run.json at {path} is unreadable: {exc}
- Updates must be a list of update objects
- Each update must be an object with todo_id
- bad_response
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/5386473bb280e8e6.
Report an issue: GitHub.