datawhalechina/hello-agents · error · MovieServiceError

TMDB 网络错误: {e}

Error message

TMDB 网络错误: {e}

What it means

MovieServiceError raised when the httpx GET raises httpx.HTTPError that is not a TimeoutException — i.e. connection-level failures: DNS resolution errors (ConnectError wrapping socket.gaierror), TLS certificate failures, connection refused/reset, or protocol errors. Note the ordering matters: TimeoutException is a subclass scenario handled first, so this branch catches the remaining transport errors.

Source

Thrown at Co-creation-projects/aatanxiao12-beep-YingQian/backend/app/services/movie_service.py:120

        else:
            params["api_key"] = api_key  # type: ignore[assignment]
        return headers, params

    def _get(self, path: str, extra_params: Optional[Dict[str, Any]] = None) -> dict:
        """对 TMDB 发 GET,统一处理超时、网络错误与非 2xx / 非 JSON。"""
        headers, params = self._auth_headers_and_params()
        if extra_params:
            for k, v in extra_params.items():
                if v is not None and v != "":
                    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

View on GitHub (pinned to 606a07d341)

Solutions

  1. Verify basic reachability: curl -I https://api.themoviedb.org/3/configuration from the same host/container
  2. Unset or fix proxy env vars (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY) if they point at a dead proxy; or configure trust_env=False
  3. For TLS interception, provide the corporate CA bundle: httpx.Client(verify='/path/to/ca.pem')
  4. Distinguish DNS vs TLS vs refused in the message by including type(e).__name__

Example fix

# before
except httpx.HTTPError as e:
    raise MovieServiceError(f"TMDB 网络错误: {e}") from e

# after
except httpx.ConnectError as e:
    raise MovieServiceError(
        f"TMDB 连接失败(DNS/网络不可达): {type(e).__name__}: {e}",
        status_code=502,
    ) from e
except httpx.HTTPError as e:
    raise MovieServiceError(f"TMDB 网络错误: {e}") from e
Defensive patterns

Strategy: retry

Validate before calling

# preflight reachability (cheap TCP/DNS check, not per-request)
import socket

def tmdb_reachable(host: str = 'api.themoviedb.org') -> bool:
    try:
        socket.getaddrinfo(host, 443)
        return True
    except socket.gaierror:
        return False

Try / catch

except MovieServiceError as e:
    if '网络错误' in str(e):
        # transport-level: one retry, then degrade
        return fallback_cache_or_empty()
    raise

Prevention

When it happens

Trigger: No internet or DNS failure resolving api.themoviedb.org; TLS interception proxy presenting an untrusted certificate (SSLError); connection reset by firewall/GFW-style filtering; proxy misconfiguration (httpx honors HTTP_PROXY env vars, and a dead proxy yields ConnectError); IPv6-only environment where TMDB is unreachable.

Common situations: Laptop on captive-portal Wi-Fi; corporate TLS-inspection with a custom CA not in certifi's bundle; HTTP_PROXY set in the environment pointing to a stopped local proxy; cloud egress blocked by security group.

Related errors


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