{"record":{"id":"4ff327ac53895b89","repo":"datawhalechina/hello-agents","slug":"tmdb-env-tmdb-access-token-tmdb-api-k","errorCode":null,"errorMessage":"TMDB 未配置：请在 .env 设置 TMDB_ACCESS_TOKEN 或 TMDB_API_KEY","messagePattern":"TMDB 未配置：请在 \\.env 设置 TMDB_ACCESS_TOKEN 或 TMDB_API_KEY","errorType":"http","errorClass":"MovieServiceError","httpStatus":503,"severity":"error","filePath":"Co-creation-projects/aatanxiao12-beep-YingQian/backend/app/services/movie_service.py","lineNumber":90,"sourceCode":"class MovieService:\n    \"\"\"TMDB 电影查询服务：search / discover / 类型名 id 映射。\"\"\"\n\n    def __init__(self, settings: Optional[Settings] = None) -> None:\n        \"\"\"初始化配置、类型缓存占位，以及复用的 httpx 客户端。\"\"\"\n        self.settings = settings or get_settings()\n        self._genre_id_to_name: Optional[Dict[int, str]] = None\n        self._genre_name_to_id: Optional[Dict[str, int]] = None\n        self._client = httpx.Client(timeout=30.0)\n\n    def close(self) -> None:\n        \"\"\"关闭底层 HTTP 客户端（进程退出或测试 teardown 时调用）。\"\"\"\n        self._client.close()\n\n    def _auth_headers_and_params(self) -> tuple[dict, dict]:\n        \"\"\"组装鉴权：优先 Bearer Access Token，否则 query 带 api_key。\"\"\"\n        token, api_key = self.settings.resolve_tmdb_credentials()\n        if not token and not api_key:\n            raise MovieServiceError(\n                \"TMDB 未配置：请在 .env 设置 TMDB_ACCESS_TOKEN 或 TMDB_API_KEY\",\n                status_code=503,\n            )\n\n        headers: dict = {\"Accept\": \"application/json\"}\n        params: dict = {\n            \"language\": self.settings.tmdb_language,\n            \"include_adult\": str(self.settings.tmdb_include_adult).lower(),\n        }\n        if token:\n            headers[\"Authorization\"] = f\"Bearer {token}\"\n        else:\n            params[\"api_key\"] = api_key  # type: ignore[assignment]\n        return headers, params\n\n    def _get(self, path: str, extra_params: Optional[Dict[str, Any]] = None) -> dict:\n        \"\"\"对 TMDB 发 GET，统一处理超时、网络错误与非 2xx / 非 JSON。\"\"\"\n        headers, params = self._auth_headers_and_params()","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/aatanxiao12-beep-YingQian/backend/app/services/movie_service.py#L72-L108","documentation":"MovieServiceError (HTTP 503) raised inside _auth_headers_and_params when settings.resolve_tmdb_credentials() returns neither a Bearer access token nor an api_key. It is a configuration guard: the service knows it cannot authenticate any TMDB request, so it fails fast with a service-unavailable semantic instead of sending a doomed request.","triggerScenarios":"Any MovieService call (get_detail, search, discover) when neither TMDB_ACCESS_TOKEN nor TMDB_API_KEY is present in the environment/.env loaded by pydantic-settings; .env exists but is not on the loaded path (wrong cwd, env_file misconfigured); variable set to an empty string, which resolve_tmdb_credentials treats as missing; keys defined only in the shell that started a different process (systemd unit, docker compose without env_file).","commonSituations":"Local dev works but container deployment lacks env passthrough; .env renamed or placed in backend/ vs repo root; CI test suite running without secrets; empty-string value left after commenting out a key.","solutions":["Set TMDB_ACCESS_TOKEN (v4 Bearer token, preferred) or TMDB_API_KEY (v3 key) in backend/.env and confirm the settings module loads that exact file","Verify with a quick check: python -c \"from app.config import get_settings; print(get_settings().resolve_tmdb_credentials())\" — should return a non-empty tuple","For docker compose, add env_file: .env or explicit environment entries to the backend service","Fail fast at startup: validate credentials in app lifespan and log a clear message instead of discovering it per-request"],"exampleFix":"# before\n# .env (backend root)\n# TMDB_ACCESS_TOKEN=\n\n# after\n# .env\nTMDB_ACCESS_TOKEN=eyJhbGciOi...your_v4_read_access_token\n\n# optional startup guard in main.py lifespan:\nfrom .config import get_settings\nfrom .services.movie_service import MovieServiceError\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    token, key = get_settings().resolve_tmdb_credentials()\n    if not token and not key:\n        raise RuntimeError(\"TMDB 未配置：请在 .env 设置 TMDB_ACCESS_TOKEN 或 TMDB_API_KEY\")\n    yield","handlingStrategy":"validation","validationCode":"from app.config import get_settings\n\ndef tmdb_configured() -> bool:\n    token, key = get_settings().resolve_tmdb_credentials()\n    return bool(token or key)","typeGuard":null,"tryCatchPattern":"from app.services.movie_service import MovieServiceError\n\ntry:\n    detail = service.get_detail(mid)\nexcept MovieServiceError as e:\n    if e.status_code == 503 and 'TMDB 未配置' in str(e):\n        return degrade_to_no_metadata(mid)  # or a friendly setup notice\n    raise","preventionTips":["Check tmdb_configured() at startup and log a loud warning instead of failing per request","Give pydantic-settings an explicit env_file path so cwd cannot hide the .env","Document both accepted variables (TMDB_ACCESS_TOKEN preferred, TMDB_API_KEY fallback) in README"],"tags":["tmdb","configuration","api-key","http-503","python"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}