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

  1. 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
  2. For 5xx: retry with exponential backoff + jitter, 2-3 attempts, since TMDB incidents are usually brief
  3. Check the resp.text snippet in the message — TMDB states the exact reason (e.g. 'Too Many Requests')
  4. 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

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


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/07457552bb87c037. Report an issue: GitHub.