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
- Rewrite vulnerabilities.json as a JSON array of report objects: [ {...}, {...} ].
- If an object with a key like 'vulnerabilities' was written, extract that array into the file root.
- If uncertain of the expected shape, generate a fresh run and compare its vulnerabilities.json structure.
- 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
- Keep vulnerabilities.json an array of objects; never wrap it in a keyed object.
- Validate external tooling that writes into run dirs against a fresh scan's file shape.
- Add a JSON-schema check in CI when artifacts are archived.
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
- vulnerabilities.json at {path} is corrupt ({exc}); refusing
- run.json at {path} is unreadable: {exc}
- run.json at {path} is not an object
- bad_response
- Cannot resume scan {scan_id}: agents.json is unreadable: {ex
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/048ba2cfc2046026.
Report an issue: GitHub.