usestrix/strix · error · RuntimeError

vulnerabilities.json at {path} is not a list

Error message

vulnerabilities.json at {path} is not a list

What it means

After successfully parsing vulnerabilities.json, ReportState requires the top-level JSON value to be a list (the file is an array of vulnerability report objects). Any other JSON type (object, string, number) raises RuntimeError. This guards the hydration contract before indexing saved report ids.

Source

Thrown at strix/report/state.py:204

            scan_results = data.get("scan_results")
            if isinstance(scan_results, dict):
                self.scan_results = scan_results
                self.final_scan_result = self._format_final_scan_result(scan_results)
            self._hydrate_llm_usage(data.get("llm_usage"))
            logger.info("report state hydrated run.json from %s", run_dir)

        json_path = run_dir / "vulnerabilities.json"
        if json_path.exists():
            try:
                data = json.loads(json_path.read_text(encoding="utf-8"))
            except (OSError, json.JSONDecodeError) as exc:
                raise RuntimeError(
                    f"vulnerabilities.json at {json_path} is corrupt ({exc}); "
                    f"refusing to start fresh — that would overwrite prior "
                    f"vulnerability MDs on disk. Inspect or delete the run dir.",
                ) from exc
            if not isinstance(data, list):
                raise RuntimeError(
                    f"vulnerabilities.json at {json_path} is not a list",
                )
            self.vulnerability_reports = [r for r in data if isinstance(r, dict)]
            for r in self.vulnerability_reports:
                rid = r.get("id")
                if isinstance(rid, str):
                    self._saved_vuln_ids.add(rid)
            logger.info(
                "report state hydrated %d vulnerability report(s)",
                len(self.vulnerability_reports),
            )

    def add_vulnerability_report(
        self,
        title: str,
        severity: str,
        description: str | None = None,
        impact: str | None = None,

View on GitHub (pinned to 8551339130)

Solutions

  1. Rewrite vulnerabilities.json as a JSON array of report objects: [ {...}, {...} ].
  2. If an object with a key like 'vulnerabilities' was written, extract that array into the file root.
  3. If uncertain of the expected shape, generate a fresh run and compare its vulnerabilities.json structure.
  4. Delete the run dir if the run is disposable.

Example fix

// before: vulnerabilities.json
{"vulnerabilities": [{"id": "VULN-001"}]}

// after: vulnerabilities.json
[{"id": "VULN-001"}]
Defensive patterns

Strategy: type-guard

Validate before calling

import json
from pathlib import Path

def vuln_json_is_list(run_dir: Path) -> bool:
    p = run_dir / "vulnerabilities.json"
    if not p.exists():
        return True
    try:
        return isinstance(json.loads(p.read_text(encoding="utf-8")), list)
    except (OSError, json.JSONDecodeError):
        return False

Type guard

def is_vuln_file_shape(data: object) -> bool:
    return isinstance(data, list) and all(isinstance(r, dict) for r in data)

Try / catch

try:
    state = ReportState(run_dir)
except RuntimeError as exc:
    if "is not a list" in str(exc):
        # repair shape: extract array from wrapper object, then retry
        ...

Prevention

When it happens

Trigger: vulnerabilities.json parses as valid JSON but is not an array — e.g. someone hand-edited it into an object like {"vulnerabilities": [...]}, or a different tool wrote an object-shaped file into the run dir.

Common situations: Manual editing or scripting over the run artifacts; schema drift after upgrading Strix; merging outputs from another reporting tool into the same run dir.

Related errors


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