datawhalechina/hello-agents · error · MovieServiceError
TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}
Error message
TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]} What it means
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.
Source
Thrown at Co-creation-projects/aatanxiao12-beep-YingQian/backend/app/services/movie_service.py:130
params[k] = v
url = f"{TMDB_API_BASE}{path}"
try:
resp = self._client.get(url, headers=headers, params=params)
except httpx.TimeoutException as e:
raise MovieServiceError(f"TMDB 请求超时: {e}") from e
except httpx.HTTPError as e:
raise MovieServiceError(f"TMDB 网络错误: {e}") from e
if resp.status_code == 401:
raise MovieServiceError(
"TMDB 鉴权失败:请检查 Access Token / API Key",
status_code=401,
)
if resp.status_code == 404:
raise MovieServiceError("影片不存在或已下架", status_code=404)
if resp.status_code >= 400:
raise MovieServiceError(
f"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}"
)
try:
return resp.json()
except ValueError as e:
raise MovieServiceError("TMDB 返回非 JSON") from e
def _poster_url(self, poster_path: Optional[str]) -> Optional[str]:
"""把 TMDB 相对 poster_path 拼成可访问的完整图片 URL。"""
if not poster_path:
return None
base = self.settings.tmdb_image_base_url.rstrip("/")
return f"{base}{poster_path}"
def _parse_year(self, release_date: Optional[str]) -> Optional[int]:
"""从 release_date(YYYY-MM-DD)解析上映年份。"""
if not release_date or len(release_date) < 4:View on GitHub (pinned to 606a07d341)
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
Example fix
# before
if resp.status_code >= 400:
raise MovieServiceError(
f"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}"
)
# after
if resp.status_code == 429:
wait = float(resp.headers.get("Retry-After", 2))
raise MovieServiceError(
f"TMDB 限流(429),{wait}s 后重试", status_code=429
)
if resp.status_code >= 500:
raise MovieServiceError(
f"TMDB 服务错误: HTTP {resp.status_code}, {resp.text[:200]}",
status_code=502,
)
if resp.status_code >= 400:
raise MovieServiceError(
f"TMDB 请求失败: HTTP {resp.status_code}, {resp.text[:200]}"
) Defensive patterns
Strategy: retry
Try / catch
except MovieServiceError as e:
if 'HTTP 429' in str(e):
time.sleep(float(re.search(r'Retry-After', ...) or 2))
return retry_once()
if 'HTTP 5' in str(e):
return retry_with_backoff()
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- API 请求频率已达上限(429 Too Many Requests)。\nSemantic Scholar 免费额度为
- LLM思考失败: {str(e)}
- ArXiv API请求失败: {response.status}
- IEEE API请求失败: {response.status}
- TMDB 未配置:请在 .env 设置 TMDB_ACCESS_TOKEN 或 TMDB_API_KEY
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/07457552bb87c037.
Report an issue: GitHub.