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

  1. Inspect run.json and rewrite it as a single JSON object with the expected keys (status, llm_usage, scan_results, ...).
  2. If unrecoverable, delete the run dir or regenerate the record with a new scan.
  3. 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

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


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/5386473bb280e8e6. Report an issue: GitHub.