BerriAI/litellm · critical · HTTPException

Invalid API key

Error message

Invalid API key

What it means

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.

Source

Thrown at cookbook/mock_prompt_management_server/mock_prompt_management_server.py:189

    Returns:
        True if valid, raises HTTPException if invalid
    """
    if authorization is None:
        # Allow requests without authentication for testing
        return True

    # Extract token from "Bearer <token>"
    if not authorization.startswith("Bearer "):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authorization header format. Expected 'Bearer <token>'",
        )

    token = authorization.replace("Bearer ", "").strip()

    if token not in VALID_API_TOKENS:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid API key",
        )

    return True


@app.get("/beta/litellm_prompt_management", response_model=PromptResponse)
async def get_prompt(
    prompt_id: str = Query(..., description="The ID of the prompt to fetch"),
    project_name: Optional[str] = Query(
        None, description="Optional project name filter"
    ),
    slug: Optional[str] = Query(None, description="Optional slug filter"),
    version: Optional[str] = Query(None, description="Optional version filter"),
    authorization: Optional[str] = Header(None),
) -> PromptResponse:
    """

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Set OPENAI_API_KEY in the executing environment.
  2. Or pass api_key=... to list_fine_tuning_jobs.
  3. Or provide a prebuilt client via client=.

Example fix

# before
jobs = litellm.list_fine_tuning_jobs(limit=20)

# after
jobs = litellm.list_fine_tuning_jobs(limit=20, api_key=os.environ["OPENAI_API_KEY"])
Defensive patterns

Strategy: validation

Validate before calling

import os, litellm

def can_list_jobs() -> bool:
    return bool(os.environ.get("OPENAI_API_KEY") or litellm.api_key or litellm.openai_key)

Try / catch

try:
    jobs = litellm.list_fine_tuning_jobs(limit=20)
except ValueError as e:
    if "not initialized" in str(e):
        jobs = litellm.list_fine_tuning_jobs(limit=20, api_key=os.environ["OPENAI_API_KEY"])
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/ad3bb719000eea0a. Report an issue: GitHub.