BerriAI/litellm · critical · Exception

Connect Proxy to database to generate keys - https://docs.li

Error message

Connect Proxy to database to generate keys - https://docs.litellm.ai/docs/proxy/virtual-keys 

What it means

generate_key_helper_fn is the core behind POST /key/generate and /user/new; virtual keys are DB records, so it immediately raises this Exception when prisma_client is None. If you see it, the proxy has no database attached, making all key generation impossible (the UI's 'Create Key' button fails the same way).

Source

Thrown at litellm/proxy/management_endpoints/key_management_endpoints.py:3805

    table_name: Literal["key", "user"] | None = None,
    send_invite_email: bool | None = None,
    created_by: str | None = None,
    updated_by: str | None = None,
    allowed_routes: list | None = None,
    key_type: str | None = None,
    sso_user_id: str | None = None,
    object_permission_id: str | None = None,  # object_permission_id <-> LiteLLM_ObjectPermissionTable
    object_permission: LiteLLM_ObjectPermissionBase | None = None,
    auto_rotate: bool | None = None,
    rotation_interval: str | None = None,
    router_settings: dict | None = None,
    access_group_ids: list[str] | None = None,
    budget_limits: list | None = None,  # multiple concurrent budget windows
):
    from litellm.proxy.proxy_server import premium_user, prisma_client

    if prisma_client is None:
        raise Exception("Connect Proxy to database to generate keys - https://docs.litellm.ai/docs/proxy/virtual_keys ")

    if token is None:
        if key is not None:
            token = key
        else:
            token = f"sk-{secrets.token_urlsafe(LENGTH_OF_LITELLM_GENERATED_KEY)}"

    if duration is None:  # allow tokens that never expire
        expires = None
    else:
        # Add duration to current time for exact expiration (not standardized reset time)
        duration_seconds: Final = duration_in_seconds(duration)
        expires = datetime.now(timezone.utc) + timedelta(seconds=duration_seconds)

    if key_budget_duration is None:  # one-time budget
        key_reset_at = None
    else:
        key_reset_at = get_budget_reset_time(budget_duration=key_budget_duration)

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Set DATABASE_URL to a Postgres connection string (or add database_connection.url in the config) and restart the proxy.
  2. Verify the DB is reachable from the proxy and Prisma migrations applied (proxy logs 'Prisma DB Connected' on startup).
  3. Add a startup/health assertion on DB connectivity in your deployment before enabling key issuance.

Example fix

# before
# no database configured
client.post("/key/generate", json={"models": ["gpt-4o"]})

# after
export DATABASE_URL="postgresql://user:pass@db:5432/litellm"
# restart proxy, then:
client.post("/key/generate", json={"models": ["gpt-4o"]})
Defensive patterns

Strategy: validation

Validate before calling

if not os.environ.get("DATABASE_URL"):
    raise RuntimeError("key generation requires a Postgres DB: set DATABASE_URL before starting litellm")
key = client.post("/key/generate", json={"models": ["gpt-4o"]}).json()

Try / catch

try:
    key = client.post("/key/generate", json={"models": ["gpt-4o"]})
    key.raise_for_status()
except HTTPError as e:
    if "Connect Proxy to database" in e.response.text:
        raise RuntimeError("litellm has no DB attached; provisioning blocked") from e
    raise

Prevention

When it happens

Trigger: POST /key/generate (or any flow that creates a key, including SSO user auto-provisioning) on a proxy started without DATABASE_URL / database config; DB env var lost in a container restart or redeploy.

Common situations: Fresh local install without Postgres; UI enabled but DB never wired; DATABASE_URL present in shell but proxy run under systemd/k8s with a sanitized env; migration to a new orchestrator dropped the secret.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/e49d74f163896fdf. Report an issue: GitHub.