{"record":{"id":"b0d019c4caa84c10","repo":"usestrix/strix","slug":"cannot-read-spec-p-exc","errorCode":null,"errorMessage":"Cannot read spec {p}: {exc}","messagePattern":"Cannot read spec (.+?): (.+?)","errorType":"exception","errorClass":"SpecParseError","httpStatus":null,"severity":"error","filePath":"strix/utils/api_spec.py","lineNumber":50,"sourceCode":"#: Guard against pathological Postman folder nesting.\n_MAX_POSTMAN_DEPTH = 25\n\n\nclass SpecParseError(ValueError):\n    \"\"\"Raised when a spec cannot be read, recognized, or fetched.\"\"\"\n\n\ndef load_spec(path: str | Path) -> dict[str, Any]:\n    \"\"\"Load an API spec file as a mapping.\n\n    Raises :class:`SpecParseError` if the file cannot be read or is not a\n    JSON/YAML mapping.\n    \"\"\"\n    p = Path(path)\n    try:\n        text = p.read_text(encoding=\"utf-8\")\n    except OSError as exc:\n        raise SpecParseError(f\"Cannot read spec {p}: {exc}\") from exc\n    # JSON is a subset of YAML, so safe_load parses both; try JSON first for a\n    # clearer error and to keep the fast path fast.\n    try:\n        data: Any = json.loads(text)\n    except json.JSONDecodeError:\n        try:\n            data = yaml.safe_load(text)\n        except yaml.YAMLError as exc:\n            raise SpecParseError(f\"{p} is not valid JSON or YAML: {exc}\") from exc\n    if not isinstance(data, dict):\n        raise SpecParseError(f\"{p} does not contain a mapping at the top level\")\n    return data\n\n\ndef classify_spec(raw: dict[str, Any]) -> str | None:\n    \"\"\"Return ``openapi`` / ``swagger`` / ``postman``, or ``None`` if unrecognized.\"\"\"\n    if isinstance(raw.get(\"openapi\"), str):\n        return \"openapi\"","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/utils/api_spec.py#L32-L68","documentation":"SpecParseError raised by strix.utils.api_spec.load_spec when the API spec file cannot be read — missing path, no permission, or any other OS-level I/O failure. The original OSError is chained as __cause__ so the underlying reason (ENOENT, EACCES, IsADirectoryError) is visible.","triggerScenarios":"Passing a spec path that does not exist (strix --api-spec ./openapi.yml typo), a path inside the Docker sandbox that was not mounted, a directory instead of a file, or an unreadable file (mode 000).","commonSituations":"Relative paths resolved against a different working directory inside the container; file present locally but outside the mounted volume; CI checkout missing the spec file.","solutions":["Check the path exists and is a file before invoking the scan","Use an absolute path, or verify the file is mounted into the sandbox when running via Docker","Inspect err.__cause__ for the exact OSError reason"],"exampleFix":"# before\nstrix -t https://api.example.com --api-spec specs/openapi.yaml  # wrong cwd\n\n# after\nstrix -t https://api.example.com --api-spec \"$(pwd)/specs/openapi.yaml\"","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\np = Path(spec_path)\nif not p.is_file():\n    raise FileNotFoundError(f\"spec not found: {p.resolve()}\")","typeGuard":null,"tryCatchPattern":"from strix.utils.api_spec import SpecParseError\n\ntry:\n    spec = load_spec(path)\nexcept SpecParseError as e:\n    if str(e).startswith(\"Cannot read spec\"):\n        resolve the absolute path / mount the file, then retry once","preventionTips":["Resolve spec paths to absolute before passing to CLI/Docker runs","Add a CI step asserting the spec file exists in the checkout","Mount the spec into the sandbox container explicitly"],"tags":["file-io","api-spec","configuration"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}