BerriAI/litellm · error · HTTPException

Invalid authorization header format. Expected 'Bearer <token

Error message

Invalid authorization header format. Expected 'Bearer <token>'

What it means

The async-client type guard on the cancel path: with _is_async=True, cancel_fine_tuning_job requires an AsyncOpenAI or AsyncAzureOpenAI client. If a synchronous client was resolved or passed, awaiting the cancel call would fail at runtime, so this ValueError raises early with an explicit message. Purely a local client-type mismatch.

Source

Thrown at cookbook/mock_prompt_management_server/mock_prompt_management_server.py:181


def verify_api_key(authorization: Optional[str] = Header(None)) -> bool:
    """
    Verify the API key from the Authorization header.

    Args:
        authorization: Authorization header (Bearer token)

    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"),

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Construct and pass AsyncOpenAI(api_key=...) for async cancellation.
  2. Or omit the custom client and let litellm create the async client from the environment key.
  3. Or keep cancellation synchronous with cancel_fine_tuning_job().

Example fix

# before
await litellm.acancel_fine_tuning_job(fine_tuning_job_id="ftjob-abc", client=OpenAI())

# after
await litellm.acancel_fine_tuning_job(fine_tuning_job_id="ftjob-abc", client=AsyncOpenAI())
Defensive patterns

Strategy: type-guard

Validate before calling

from openai import AsyncOpenAI

def assert_async_client_for_cancel(client: object) -> None:
    assert isinstance(client, AsyncOpenAI), f"expected AsyncOpenAI, got {type(client).__name__}"

Type guard

from openai import AsyncOpenAI, AsyncAzureOpenAI

def is_usable_async_client(client: object) -> bool:
    return isinstance(client, (AsyncOpenAI, AsyncAzureOpenAI))

Try / catch

try:
    await litellm.acancel_fine_tuning_job(fine_tuning_job_id=jid, client=client)
except ValueError as e:
    if "AsyncOpenAI" in str(e):
        from openai import AsyncOpenAI
        await litellm.acancel_fine_tuning_job(fine_tuning_job_id=jid, client=AsyncOpenAI())
    else:
        raise

Prevention

When it happens

Trigger: Calling the async cancel path (await litellm.acancel_fine_tuning_job(...)) while passing a sync OpenAI() client, or letting the handler resolve a sync client for an async operation.

Common situations: Async orchestration daemons reusing a sync client singleton; refactoring sync job-management code to asyncio without updating client construction; mixing sync creation and async cancellation in the same module.

Related errors


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