litellm - 💡(How to fix) Fix [Bug]: user_id_upsert silently fails when existing user has same email but different user_id [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#26789Fetched 2026-04-30 06:19:48
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
labeled ×1

Error Message

  1. The error is caught and silently swallowed — the request proceeds without a valid user object In auth_checks.py get_user_object(), the upsert path does a raw prisma_client.db.litellm_usertable.create(). This does not handle the case where user_email already exists on a different user_id. The unique constraint violation is caught by a broad except Exception and the function raises a generic ValueError("User doesn't exist in db...") or silently continues, depending on the code path.
  • Silent failure — no error logs at INFO level, no indication anything is wrong

Root Cause

In auth_checks.py get_user_object(), the upsert path does a raw prisma_client.db.litellm_usertable.create(). This does not handle the case where user_email already exists on a different user_id. The unique constraint violation is caught by a broad except Exception and the function raises a generic ValueError("User doesn't exist in db...") or silently continues, depending on the code path.

The fix should either:

  • Use upsert instead of create (match on user_email if user_id lookup fails)
  • Or check for an existing record by user_email before attempting create

Related: #21920 — same function, different failure mode. The reporter there also suggests using SSOAuthenticationHandler.upsert_sso_user or internal_user_endpoints.new_user which properly handle duplicates.

Code Example

user_id: bb8ab11f-09aa-47ae-b063-6e80506ac3bc
user_email: matt@example.com
user_role: proxy_admin

---

curl -X POST /user/new -d '{
  "user_id": "bb8ab11f-09aa-47ae-b063-6e80506ac3bc",
  "user_email": "[email protected]",
  "user_role": "proxy_admin"
}'

---

general_settings:
  enable_jwt_auth: true
  litellm_jwtauth:
    user_id_jwt_field: "email"
    user_id_upsert: true
    team_id_default: "some-team-id"

---

# The email-based lookup fails:
GET /user/info?user_id=matt@example.com404 "User not found"

# The UUID-based record still exists but has no new spend:
GET /user/info?user_id=bb8ab11f-...  → spend: $0.00 (no JWT spend attributed)
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

When user_id_upsert: true is enabled for JWT auth, the upsert silently fails if a user record already exists with the same user_email but a different user_id. This causes all spend from JWT-authenticated requests to be orphaned — tracked at the team/key level but never attributed to the user. The Usage page shows $0 for affected users even though they are actively making requests.

This is an auditing and cost-tracking issue — we have 10 users in production whose spend is completely untracked at the user level.

Background

We migrated from API-key-based auth to JWT auth (Google OIDC). Before the migration, users were created with UUID-based user_id values and their user_email set separately:

user_id: bb8ab11f-09aa-47ae-b063-6e80506ac3bc
user_email: [email protected]
user_role: proxy_admin

After enabling JWT auth with user_id_jwt_field: "email", the JWT handler extracts user_id = "[email protected]" from the token. The upsert logic in get_user_object() then:

  1. Looks up user by user_id = "[email protected]"NOT FOUND
  2. Tries prisma_client.db.litellm_usertable.create(user_id="[email protected]", user_email="[email protected]", ...)
  3. Fails because user_email has a unique constraint and "[email protected]" already exists on the UUID-based record
  4. The error is caught and silently swallowed — the request proceeds without a valid user object
  5. Spend is recorded against the team/key but not attributed to any user

Users who were created fresh after the JWT migration (no pre-existing UUID record) work correctly — the create() succeeds and spend tracks properly.

Steps to Reproduce

  1. Create a user with a UUID user_id and set their user_email:
curl -X POST /user/new -d '{
  "user_id": "bb8ab11f-09aa-47ae-b063-6e80506ac3bc",
  "user_email": "[email protected]",
  "user_role": "proxy_admin"
}'
  1. Enable JWT auth with user_id_upsert:
general_settings:
  enable_jwt_auth: true
  litellm_jwtauth:
    user_id_jwt_field: "email"
    user_id_upsert: true
    team_id_default: "some-team-id"
  1. Make an API request with a JWT where email = "[email protected]"

  2. Check user info:

# The email-based lookup fails:
GET /user/info?user_id=[email protected]404 "User not found"

# The UUID-based record still exists but has no new spend:
GET /user/info?user_id=bb8ab11f-...  → spend: $0.00 (no JWT spend attributed)
  1. Check the Usage page → shows $0 for this user

Root Cause

In auth_checks.py get_user_object(), the upsert path does a raw prisma_client.db.litellm_usertable.create(). This does not handle the case where user_email already exists on a different user_id. The unique constraint violation is caught by a broad except Exception and the function raises a generic ValueError("User doesn't exist in db...") or silently continues, depending on the code path.

The fix should either:

  • Use upsert instead of create (match on user_email if user_id lookup fails)
  • Or check for an existing record by user_email before attempting create

Related: #21920 — same function, different failure mode. The reporter there also suggests using SSOAuthenticationHandler.upsert_sso_user or internal_user_endpoints.new_user which properly handle duplicates.

Impact

  • 10 users affected in our deployment (everyone who had a pre-JWT UUID-based record)
  • All JWT-authenticated spend is orphaned for these users — tracked at team/key level but not at user level
  • Usage page shows $0 for affected users despite active usage
  • Silent failure — no error logs at INFO level, no indication anything is wrong
  • This is a cost auditing gap — we cannot attribute spend to individual users

Relevant log output

No errors logged at INFO level. The failure is completely silent. At DEBUG level you may see a Prisma DataError about unique constraint violation, but it's caught and swallowed.

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on?

v1.83.3-stable

Twitter / LinkedIn details

No response

extent analysis

TL;DR

The issue can be fixed by modifying the get_user_object() function in auth_checks.py to use upsert instead of create when user_id_upsert is enabled, matching on user_email if user_id lookup fails.

Guidance

  • Modify the get_user_object() function to handle the case where user_email already exists on a different user_id by using upsert with a match on user_email.
  • Alternatively, check for an existing record by user_email before attempting create to avoid the unique constraint violation.
  • Consider using SSOAuthenticationHandler.upsert_sso_user or internal_user_endpoints.new_user which properly handle duplicates, as suggested in the related issue #21920.
  • Verify the fix by checking the Usage page for affected users and ensuring that spend is correctly attributed to individual users.

Example

# Example of using upsert with match on user_email
prisma_client.db.litellm_usertable.upsert(
    where={user_email: "[email protected]"},
    create={user_id: "[email protected]", user_email: "[email protected]"},
    update={user_id: "[email protected]"}
)

Notes

The provided solution assumes that the upsert method is available in the Prisma client. If not, an alternative approach would be to manually check for an existing record by user_email before attempting create.

Recommendation

Apply the workaround by modifying the get_user_object() function to use upsert instead of create when user_id_upsert is enabled, as this directly addresses the root cause of the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - 💡(How to fix) Fix [Bug]: user_id_upsert silently fails when existing user has same email but different user_id [1 participants]