BerriAI/litellm · error · HTTPException

Prompt '{prompt.prompt_id}' already exists

Error message

Prompt '{prompt.prompt_id}' already exists

What it means

The async-client type guard on the retrieve path, and stricter than the other fine-tuning guards: it accepts only AsyncOpenAI - not AsyncAzureOpenAI. When _is_async=True and the resolved client is anything else (sync OpenAI, or even an Azure async client), this ValueError is raised before the awaited retrieve call. It exists because the subsequent code casts directly to OpenAI's retrieve API.

Source

Thrown at cookbook/mock_prompt_management_server/mock_prompt_management_server.py:343

            "prompt_variables": {var: f"<{var}_value>" for var in variables},
        },
    }


@app.post("/prompts")
async def create_prompt(
    prompt: PromptResponse, authorization: Optional[str] = Header(None)
):
    """
    Create a new prompt (convenience endpoint for testing).

    This is NOT part of the LiteLLM spec - it's just for testing purposes.
    """
    # Verify authentication
    verify_api_key(authorization)

    if prompt.prompt_id in PROMPTS_DB:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Prompt '{prompt.prompt_id}' already exists",
        )

    PROMPTS_DB[prompt.prompt_id] = prompt.dict()

    return {
        "status": "created",
        "prompt_id": prompt.prompt_id,
        "message": "Prompt created successfully (in-memory only)",
    }


# ============================================================================
# Main
# ============================================================================

if __name__ == "__main__":

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Pass client=AsyncOpenAI(api_key=...) explicitly for async retrieve.
  2. Azure users: use the sync retrieve_fine_tuning_job() or route through the Azure-specific fine-tuning path.
  3. If no custom client is needed, rely on OPENAI_API_KEY so litellm constructs the correct async client.

Example fix

# before
job = await litellm.aretrieve_fine_tuning_job("ftjob-abc", client=OpenAI())

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

Strategy: type-guard

Validate before calling

from openai import AsyncOpenAI

def is_first_party_async_client(client: object) -> bool:
    # stricter than other paths: AsyncAzureOpenAI is NOT accepted here
    return type(client) is AsyncOpenAI

Type guard

from openai import AsyncOpenAI

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

Try / catch

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

Prevention

When it happens

Trigger: Calling the async retrieve path with a synchronous OpenAI client, or with an AsyncAzureOpenAI client (which this path does not accept), or when a non-async client is resolved from the environment.

Common situations: Porting sync retrieval code to async without swapping client classes; Azure fine-tuning users hitting the async path that only supports the first-party OpenAI async client; shared client factories returning the wrong class.

Related errors


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