{"record":{"id":"67120f6f20484b2f","repo":"usestrix/strix","slug":"p-is-not-valid-json-or-yaml-exc","errorCode":null,"errorMessage":"{p} is not valid JSON or YAML: {exc}","messagePattern":"(.+?) is not valid JSON or YAML: (.+?)","errorType":"exception","errorClass":"SpecParseError","httpStatus":null,"severity":"error","filePath":"strix/utils/api_spec.py","lineNumber":59,"sourceCode":"    \"\"\"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\"\n    if str(raw.get(\"swagger\", \"\")).startswith(\"2\"):\n        return \"swagger\"\n    info = raw.get(\"info\")\n    if isinstance(info, dict) and (\"_postman_id\" in info or \"item\" in raw):\n        return \"postman\"\n    return None\n\n\ndef detect_spec_format(path: Path) -> str | None:","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/utils/api_spec.py#L41-L77","documentation":"SpecParseError raised by load_spec when the file's text parses as neither JSON nor YAML (JSON is tried first for a clearer error; then yaml.safe_load). The chained YAMLError pinpoints the offending line/column. Note YAML is permissive, so this usually means genuinely malformed content, not a minor syntax slip.","triggerScenarios":"A spec file containing an HTML error page (proxy intercepted the download), a truncated download, binary/UTF-16 content, or JSON with a trailing comma and invalid YAML characters.","commonSituations":"Downloading an OpenAPI spec from a URL that returned a login page; CI caching a partial file; a Postman export saved with a BOM or wrong encoding.","solutions":["Validate the file locally: python -c \"import yaml,sys; yaml.safe_load(open(sys.argv[1]))\" file","Re-download the spec and confirm the content starts with '{' or 'openapi:' rather than '<'","Check the chained YAMLError for line/column of the syntax problem"],"exampleFix":"# before: file contains '<html>403 Forbidden</html>'\n\n# after: file starts with\nopenapi: 3.1.0\ninfo:\n  title: Example API","handlingStrategy":"validation","validationCode":"import json, yaml\n\ntext = Path(spec).read_text(encoding=\"utf-8\")\ntry:\n    json.loads(text)\nexcept json.JSONDecodeError:\n    try:\n        yaml.safe_load(text)\n    except yaml.YAMLError as e:\n        raise ValueError(f\"spec is not JSON/YAML: {e}\") from e","typeGuard":null,"tryCatchPattern":"try:\n    spec = load_spec(path)\nexcept SpecParseError as e:\n    if \"not valid JSON or YAML\" in str(e):\n        show e.__cause__ (YAMLError with line/col) and stop — the file content is wrong, retrying will not help","preventionTips":["curl -I the spec URL and check Content-Type/first byte before saving","Run a YAML/JSON lint step on downloaded specs in CI","Never trust a cached partial download; re-fetch on any parse failure"],"tags":["parsing","api-spec","yaml","json"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}