{"record":{"id":"8aed724e80bd2c80","repo":"usestrix/strix","slug":"postman-api-returned-http-response-status-code-f","errorCode":null,"errorMessage":"Postman API returned HTTP {response.status_code} for {label}.","messagePattern":"Postman API returned HTTP (.+?) for (.+?)\\.","errorType":"exception","errorClass":"SpecParseError","httpStatus":null,"severity":"error","filePath":"strix/utils/api_spec.py","lineNumber":265,"sourceCode":"            \"fetch from the Postman API, or pass a local collection file instead.\",\n        )\n    try:\n        response = requests.get(\n            url,\n            headers={\"X-Api-Key\": api_key, \"Accept\": \"application/json\"},\n            timeout=_POSTMAN_FETCH_TIMEOUT,\n        )\n    except requests.RequestException as exc:\n        raise SpecParseError(f\"Failed to reach the Postman API: {exc}\") from exc\n\n    if response.status_code == 401:\n        raise SpecParseError(\"Postman API rejected the key (401). Check POSTMAN_API_KEY.\")\n    if response.status_code == 404:\n        raise SpecParseError(\n            f\"Postman {label} not found (404). Check the id and that the key can access it.\",\n        )\n    if response.status_code != 200:\n        raise SpecParseError(f\"Postman API returned HTTP {response.status_code} for {label}.\")\n    try:\n        payload = response.json()\n    except ValueError as exc:\n        raise SpecParseError(f\"Postman API returned non-JSON for {label}\") from exc\n    if not isinstance(payload, dict):\n        raise SpecParseError(f\"Unexpected Postman API response shape for {label}\")\n    return payload\n\n\ndef fetch_postman_collection(collection_uid: str, api_key: str) -> dict[str, Any]:\n    \"\"\"Fetch a collection from the Postman API and return the raw collection dict.\n\n    Uses ``GET /collections/{uid}`` with the ``X-Api-Key`` header. The endpoint\n    wraps the collection under a ``collection`` key, unwrapped here so the result\n    matches an exported collection file.\n    \"\"\"\n    payload = _postman_api_json(\n        f\"{POSTMAN_API_BASE}/collections/{collection_uid}\",","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/utils/api_spec.py#L247-L283","documentation":"Catch-all SpecParseError for any Postman API response status other than 200/401/404 — typically 403 (permissions/plan limits), 429 (rate limit), or 5xx. The status code and resource label are included in the message.","triggerScenarios":"Hammering the API past the rate limit (429); a free-plan key hitting an endpoint it cannot use (403); Postman-side incident (500/503).","commonSituations":"Retries in a tight loop after a transient failure; shared CI key exhausted by parallel jobs; plan restrictions on API access.","solutions":["For 429: wait (check Retry-After) and retry once, and serialize CI jobs that share the key","For 403: verify the key's scope/plan permits collection API reads","For 5xx: check status.getpostman.com, then retry; or export the collection locally to remove the dependency"],"exampleFix":"# before: parallel CI jobs share one key -> 429\n\n# after: fetch once, cache the artifact\ncurl -H \"X-Api-Key: $POSTMAN_API_KEY\" .../collections/$UID > postman-cache.json  # CI cache step\nstrix --api-spec ./postman-cache.json","handlingStrategy":"retry","validationCode":"def within_rate_limit() -> bool:\n    # simple local guard; Postman does not expose headers pre-call\n    return (time.time() - last_call_ts) >= min_interval_seconds","typeGuard":null,"tryCatchPattern":"except SpecParseError as e:\n    m = re.search(r\"HTTP (\\d+)\", str(e))\n    code = int(m.group(1)) if m else 0\n    if code == 429:\n        wait on Retry-Then backoff and retry once\n    elif code >= 500:\n        retry once after a short delay\n    else:\n        surface the error (permissions/plan) — retrying will not help","preventionTips":["Serialize Postman fetches across parallel CI jobs sharing one key","Cache fetched collections as CI artifacts","Monitor for 429/403 and alert rather than silently looping"],"tags":["postman","http-status","rate-limit","retry"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}