{"record":{"id":"07457552bb87c037","repo":"datawhalechina/hello-agents","slug":"tmdb-http-resp-status-code-resp-text-20","errorCode":null,"errorMessage":"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}","messagePattern":"TMDB 请求失败: HTTP (.+?), (.+?)","errorType":"http","errorClass":"MovieServiceError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/aatanxiao12-beep-YingQian/backend/app/services/movie_service.py","lineNumber":130,"sourceCode":"                    params[k] = v\n\n        url = f\"{TMDB_API_BASE}{path}\"\n        try:\n            resp = self._client.get(url, headers=headers, params=params)\n        except httpx.TimeoutException as e:\n            raise MovieServiceError(f\"TMDB 请求超时: {e}\") from e\n        except httpx.HTTPError as e:\n            raise MovieServiceError(f\"TMDB 网络错误: {e}\") from e\n\n        if resp.status_code == 401:\n            raise MovieServiceError(\n                \"TMDB 鉴权失败：请检查 Access Token / API Key\",\n                status_code=401,\n            )\n        if resp.status_code == 404:\n            raise MovieServiceError(\"影片不存在或已下架\", status_code=404)\n        if resp.status_code >= 400:\n            raise MovieServiceError(\n                f\"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}\"\n            )\n\n        try:\n            return resp.json()\n        except ValueError as e:\n            raise MovieServiceError(\"TMDB 返回非 JSON\") from e\n\n    def _poster_url(self, poster_path: Optional[str]) -> Optional[str]:\n        \"\"\"把 TMDB 相对 poster_path 拼成可访问的完整图片 URL。\"\"\"\n        if not poster_path:\n            return None\n        base = self.settings.tmdb_image_base_url.rstrip(\"/\")\n        return f\"{base}{poster_path}\"\n\n    def _parse_year(self, release_date: Optional[str]) -> Optional[int]:\n        \"\"\"从 release_date（YYYY-MM-DD）解析上映年份。\"\"\"\n        if not release_date or len(release_date) < 4:","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/aatanxiao12-beep-YingQian/backend/app/services/movie_service.py#L112-L148","documentation":"MovieServiceError raised when TMDB returns any status >= 400 that is not 401/404. The message embeds the status code and the first 200 characters of the response body, so the TMDB error JSON (usually {\"success\":false,\"status_message\":...}) is visible. Most common members: 429 rate limiting and occasional 5xx upstream errors.","triggerScenarios":"HTTP 429 when exceeding TMDB's rate limit (roughly 40-50 requests/10s per key; bursty agent pipelines that call search + detail per candidate easily trip it); 403 for region-blocked or suspended keys; 500/502/503 during TMDB incidents; malformed query params causing 400.","commonSituations":"Recommendation pipeline fanning out over dozens of candidates without throttling; shared API key across dev + prod environments; retry storms after a transient 5xx amplifying into a 429.","solutions":["For 429: honor the Retry-After header, add a client-side rate limiter (e.g. token bucket ~30 req/s burst 10) or sleep between batch calls","For 5xx: retry with exponential backoff + jitter, 2-3 attempts, since TMDB incidents are usually brief","Check the resp.text snippet in the message — TMDB states the exact reason (e.g. 'Too Many Requests')","Give dedicated keys per environment so dev load does not starve prod"],"exampleFix":"# before\nif resp.status_code >= 400:\n    raise MovieServiceError(\n        f\"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}\"\n    )\n\n# after\nif resp.status_code == 429:\n    wait = float(resp.headers.get(\"Retry-After\", 2))\n    raise MovieServiceError(\n        f\"TMDB 限流(429)，{wait}s 后重试\", status_code=429\n    )\nif resp.status_code >= 500:\n    raise MovieServiceError(\n        f\"TMDB 服务错误: HTTP {resp.status_code}, {resp.text[:200]}\",\n        status_code=502,\n    )\nif resp.status_code >= 400:\n    raise MovieServiceError(\n        f\"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}\"\n    )","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"except MovieServiceError as e:\n    if 'HTTP 429' in str(e):\n        time.sleep(float(re.search(r'Retry-After', ...) or 2))\n        return retry_once()\n    if 'HTTP 5' in str(e):\n        return retry_with_backoff()\n    raise","preventionTips":["Client-side rate limit all TMDB calls (token bucket) so bursts never reach 429","Parse and honor Retry-After; encode the status code on MovieServiceError so callers can branch on it","Use separate keys per environment to isolate rate budgets"],"tags":["tmdb","rate-limit","http-429","http-5xx","python"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}