{"record":{"id":"ad3bb719000eea0a","repo":"BerriAI/litellm","slug":"invalid-api-key","errorCode":null,"errorMessage":"Invalid API key","messagePattern":"Invalid API key","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"critical","filePath":"cookbook/mock_prompt_management_server/mock_prompt_management_server.py","lineNumber":189,"sourceCode":"\n    Returns:\n        True if valid, raises HTTPException if invalid\n    \"\"\"\n    if authorization is None:\n        # Allow requests without authentication for testing\n        return True\n\n    # Extract token from \"Bearer <token>\"\n    if not authorization.startswith(\"Bearer \"):\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Invalid authorization header format. Expected 'Bearer <token>'\",\n        )\n\n    token = authorization.replace(\"Bearer \", \"\").strip()\n\n    if token not in VALID_API_TOKENS:\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=\"Invalid API key\",\n        )\n\n    return True\n\n\n@app.get(\"/beta/litellm_prompt_management\", response_model=PromptResponse)\nasync def get_prompt(\n    prompt_id: str = Query(..., description=\"The ID of the prompt to fetch\"),\n    project_name: Optional[str] = Query(\n        None, description=\"Optional project name filter\"\n    ),\n    slug: Optional[str] = Query(None, description=\"Optional slug filter\"),\n    version: Optional[str] = Query(None, description=\"Optional version filter\"),\n    authorization: Optional[str] = Header(None),\n) -> PromptResponse:\n    \"\"\"","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/cookbook/mock_prompt_management_server/mock_prompt_management_server.py#L171-L207","documentation":"The client-resolution guard on the list path: list_fine_tuning_jobs resolves a client through get_openai_client(), and if none can be built (no api_key arg, no OPENAI_API_KEY env/secret, no custom client), this ValueError is raised locally before any network call. It means credentials were missing from every source in the lookup chain.","triggerScenarios":"Calling litellm.list_fine_tuning_jobs() (optionally with after/limit pagination) with no API key anywhere: not in the call, not in litellm module globals, not in OPENAI_API_KEY.","commonSituations":"Monitoring/dashboard scripts polling job lists in environments without the key; notebooks where the key was set in a different kernel; multi-tenant setups expecting router keys that are not visible to the fine-tuning handler.","solutions":["Set OPENAI_API_KEY in the executing environment.","Or pass api_key=... to list_fine_tuning_jobs.","Or provide a prebuilt client via client=."],"exampleFix":"# before\njobs = litellm.list_fine_tuning_jobs(limit=20)\n\n# after\njobs = litellm.list_fine_tuning_jobs(limit=20, api_key=os.environ[\"OPENAI_API_KEY\"])","handlingStrategy":"validation","validationCode":"import os, litellm\n\ndef can_list_jobs() -> bool:\n    return bool(os.environ.get(\"OPENAI_API_KEY\") or litellm.api_key or litellm.openai_key)","typeGuard":null,"tryCatchPattern":"try:\n    jobs = litellm.list_fine_tuning_jobs(limit=20)\nexcept ValueError as e:\n    if \"not initialized\" in str(e):\n        jobs = litellm.list_fine_tuning_jobs(limit=20, api_key=os.environ[\"OPENAI_API_KEY\"])\n    else:\n        raise","preventionTips":["Dashboard/polling deployments should receive OPENAI_API_KEY via the same secret injection as the app.","Add a boot-time credential check that fails fast."],"tags":["openai","fine-tuning","authentication","configuration","litellm"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}