{"record":{"id":"d6582660009a37b9","repo":"BerriAI/litellm","slug":"f-user-not-found-user-id","errorCode":null,"errorMessage":"f\"User not found: {user_id}\"","messagePattern":"f\"User not found: (.+?)\"","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"litellm/proxy/management_endpoints/internal_user_endpoints.py","lineNumber":1042,"sourceCode":"        if user_id is None:\n            user_id = user_api_key_dict.user_id\n\n        if user_id is None:\n            raise HTTPException(\n                status_code=400,\n                detail=\"user_id is required. Either pass it as a query parameter or authenticate with a user-bound key.\",\n            )\n\n        # Check access — returns the user row if allowed, None otherwise.\n        # This avoids a redundant DB fetch since the access check already\n        # loads the target user for team-admin verification.\n        user_row: Final = await _check_user_info_v2_access(\n            user_api_key_dict=user_api_key_dict,\n            target_user_id=user_id,\n        )\n\n        if user_row is None:\n            raise HTTPException(\n                status_code=404,\n                detail=f\"User not found: {user_id}\",\n            )\n\n        user_data: Final = user_row.model_dump()\n\n        return UserInfoV2Response(\n            user_id=user_data.get(\"user_id\", user_id),\n            user_email=user_data.get(\"user_email\"),\n            user_alias=user_data.get(\"user_alias\"),\n            user_role=user_data.get(\"user_role\"),\n            spend=user_data.get(\"spend\", 0.0),\n            max_budget=user_data.get(\"max_budget\"),\n            models=user_data.get(\"models\") or [],\n            budget_duration=user_data.get(\"budget_duration\"),\n            budget_reset_at=user_data.get(\"budget_reset_at\"),\n            metadata=_redact_scim_enterprise_metadata(user_data.get(\"metadata\")),\n            created_at=user_data.get(\"created_at\"),","sourceCodeStart":1024,"sourceCodeEnd":1060,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/internal_user_endpoints.py#L1024-L1060","documentation":"GET /v2/user/info runs _check_user_info_v2_access (allows self-lookup, proxy-admin roles, and team-admin over the target) which loads the target user row and returns it when allowed, None otherwise. A None result - either the user does not exist OR the caller is not permitted - produces 404 'User not found: {user_id}'. Returning 404 for both cases avoids leaking which user ids exist.","triggerScenarios":"GET /v2/user/info?user_id=X where X was deleted or never existed; or X exists but the caller is neither X itself, a proxy admin/viewer, nor an admin of a team X belongs to.","commonSituations":"Non-admin dashboards fetching arbitrary users; stale ids after user deletion; access recently lost via team membership changes; assuming team admins can read any team member here without the team link existing.","solutions":["Verify the id exists via GET /user/list (with an admin key) and fix typos","Use a proxy-admin key, or the target user's own key for self-lookup","If team-based access is expected, confirm the caller is actually an admin of a team that contains the target user"],"exampleFix":"# before: internal-user key asking for another user -> 404\nGET /v2/user/info?user_id=other-user -H 'Authorization: Bearer sk-internal'\n\n# after: self lookup, or admin key\nGET /v2/user/info -H 'Authorization: Bearer sk-internal'\nGET /v2/user/info?user_id=other-user -H 'Authorization: Bearer sk-admin'  # 200","handlingStrategy":"validation","validationCode":"import requests\n\ndef target_readable(base_url: str, admin_headers: dict, target_user_id: str) -> bool:\n    r = requests.get(f\"{base_url}/user/list\", headers=admin_headers, timeout=10)\n    r.raise_for_status()\n    return any(u.get(\"user_id\") == target_user_id for u in r.json().get(\"data\", []))","typeGuard":"def will_v2_lookup_succeed(key_role: str, key_user_id: str | None, target: str | None) -> bool:\n    # 404 fires when target missing OR not self/admin; caller can only pre-check self/admin\n    return key_role in (\"proxy_admin\", \"proxy_admin_viewer\") or target == key_user_id","tryCatchPattern":"except requests.HTTPError as e:\n    if e.response is not None and e.response.status_code == 404:\n        # means missing OR forbidden - do not leak existence assumptions; surface as 'unavailable'\n        return None\n    raise","preventionTips":["Use admin keys for cross-user lookups; reserve user keys for self-lookup","Verify ids via /user/list before building links/reports on them","Remember v2 returns 404 (not 403) for forbidden targets - never parse it as pure existence"],"tags":["litellm","user-info","not-found","authorization"],"backgroundTag":"user-not-found","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}