usestrix/strix · error · SpecParseError
{p} does not contain a mapping at the top level
Error message
{p} does not contain a mapping at the top level What it means
SpecParseError raised by load_spec when the file parses successfully but the top-level node is not a mapping — e.g. a YAML list, a bare scalar, or a JSON array. API specs (OpenAPI, Swagger, Postman) must be objects at the root.
Source
Thrown at strix/utils/api_spec.py:61
Raises :class:`SpecParseError` if the file cannot be read or is not a
JSON/YAML mapping.
"""
p = Path(path)
try:
text = p.read_text(encoding="utf-8")
except OSError as exc:
raise SpecParseError(f"Cannot read spec {p}: {exc}") from exc
# JSON is a subset of YAML, so safe_load parses both; try JSON first for a
# clearer error and to keep the fast path fast.
try:
data: Any = json.loads(text)
except json.JSONDecodeError:
try:
data = yaml.safe_load(text)
except yaml.YAMLError as exc:
raise SpecParseError(f"{p} is not valid JSON or YAML: {exc}") from exc
if not isinstance(data, dict):
raise SpecParseError(f"{p} does not contain a mapping at the top level")
return data
def classify_spec(raw: dict[str, Any]) -> str | None:
"""Return ``openapi`` / ``swagger`` / ``postman``, or ``None`` if unrecognized."""
if isinstance(raw.get("openapi"), str):
return "openapi"
if str(raw.get("swagger", "")).startswith("2"):
return "swagger"
info = raw.get("info")
if isinstance(info, dict) and ("_postman_id" in info or "item" in raw):
return "postman"
return None
def detect_spec_format(path: Path) -> str | None:
"""Return the spec format of *path*, or ``None`` if it is not a spec.
View on GitHub (pinned to 8551339130)
Solutions
- Wrap the content in an object: the root must be a mapping such as {'openapi': '3.0.0', ...}
- If you extracted a fragment, re-export the full spec instead
- For an empty file, regenerate the spec from the source (Postman export, swagger gen)
Example fix
# before
[{"path": "/users", "method": "get"}]
# after
{"openapi": "3.0.0", "paths": {"/users": {"get": {"responses": {"200": {"description": "ok"}}}}}} Defensive patterns
Strategy: type-guard
Validate before calling
data = yaml.safe_load(Path(spec).read_text())
if not isinstance(data, dict):
raise ValueError(f"spec root is {type(data).__name__}, expected mapping") Type guard
def is_spec_mapping(data) -> bool:
return isinstance(data, dict) and len(data) > 0 Try / catch
try:
spec = load_spec(path)
except SpecParseError as e:
if "does not contain a mapping" in str(e):
stop and fix the source file — retrying with the same content cannot succeed Prevention
- When extracting spec fragments, keep the full document instead
- Assert the first non-space character of the file is '{' before parsing
When it happens
Trigger: A YAML file that is just '- item\n- item'; a JSON file containing a top-level array like '[{"path": "/users"}]'; a YAML file of just a string or number; an effectively empty file that safe_load parses as None.
Common situations: Extracting only the 'paths' array from a spec and saving that; hand-authored spec starting with a list; empty file left by a failed pipeline step.
Related errors
- {p} is not valid JSON or YAML: {exc}
- Invalid API spec '{target}': {exc}
- Missing Postman collection id in '{target}' (expected postma
- Path exists but is not a directory: {target}
- Cannot read spec {p}: {exc}
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/b0cd54977d1bac42.
Report an issue: GitHub.