BerriAI/litellm · error · HTTPException

Key not found: {hashed_token}

Error message

Key not found: {hashed_token}

What it means

Thrown from _check_key_admin_access when a NON-proxy-admin caller (e.g. a team admin) invokes /key/block or /key/unblock and the target key cannot be found in the verification token table. The helper needs the key's row to discover its team_id so it can decide team/org admin rights, so a missing row is fatal before the 403 authorization check. Proxy admins never hit this path because the function returns early for them.

Source

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

    Check that the caller has admin privileges for the target key.

    Allowed callers:
    - Proxy admin
    - Team admin for the key's team
    - Org admin for the key's team's organization

    Raises HTTPException(403) if the caller is not authorized.
    """

    if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value:
        return

    # Look up the target key to find its team
    target_key_row: Final = await _prisma_table(VerificationTokenRepository(prisma_client)).find_unique(
        where={"token": hashed_token}
    )
    if target_key_row is None:
        raise HTTPException(
            status_code=404,
            detail={"error": f"Key not found: {hashed_token}"},
        )

    # If the key belongs to a team, check team admin / org admin
    if target_key_row.team_id:
        team_obj: Final = await get_team_object(
            team_id=target_key_row.team_id,
            prisma_client=prisma_client,
            user_api_key_cache=user_api_key_cache,
            check_db_only=True,
        )
        if team_obj is not None:
            if _is_user_team_admin(user_api_key_dict=user_api_key_dict, team_obj=team_obj):
                return
            if await _is_user_org_admin_for_team(user_api_key_dict=user_api_key_dict, team_obj=team_obj):
                return

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Verify the key still exists first: GET /key/info or /key/list with the same hashed token against the same proxy instance
  2. If you meant to pass the raw key, confirm you copied the full 'sk-' value (the server hashes it automatically) — a truncated hash will not match
  3. Treat 404 on block/unblock as idempotent success if the goal is 'make sure this key cannot be used'
  4. If you are a proxy admin and still see this, check you are calling the right route — the lookup only runs for non-admin callers

Example fix

# before
await client.post('/key/block', json={'key': truncated_hash})      # 404: Key not found: <hash>
# after
info = await client.get('/key/info', params={'key': truncated_hash})
if info.status_code == 404:
    pass  # already deleted; nothing to block
else:
    await client.post('/key/block', json={'key': info.json()['token']})
Defensive patterns

Strategy: try-catch

Validate before calling

async def key_exists(client: httpx.AsyncClient, hashed_token: str) -> bool:
    r = await client.get('/key/info', params={'key': hashed_token})
    return r.status_code == 200

Try / catch

try:
    await client.post('/key/block', json={'key': tok})
except httpx.HTTPStatusError as e:
    if e.response.status_code == 404:
        pass   # key already deleted: desired end state, nothing to block
    elif e.response.status_code == 403:
        raise PermissionError('caller is not proxy/team/org admin for this key')
    else:
        raise

Prevention

When it happens

Trigger: A team admin or org admin POSTs /key/block with a key that was already deleted, was never created, or whose hashed token was mistyped. Note the raw sk-... value is hashed upstream, so this usually means the 64-hex hash is wrong (copied truncated, wrong environment's DB).

Common situations: Multi-tenant setups where a team admin blocks keys from a stale list fetched minutes earlier and the key was rotated/deleted meanwhile; scripts pointing at the wrong proxy instance (key exists in prod DB but not in staging); passing a display alias instead of the token.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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