fix(users): preserve retries for existing-user upserts

This commit is contained in:
Mrinal Chanshetty 2026-11-02 19:33:50 -08:00
parent e2ecf2b813
commit d259be2019
2 changed files with 25 additions and 4 deletions

View file

@ -311,6 +311,7 @@ if TYPE_CHECKING:
from prisma import models as prisma_models
from prisma.actions import LiteLLM_DeprecatedVerificationTokenActions
from prisma.client import TransactionManager
from prisma.errors import UniqueViolationError
from prisma.models import LiteLLM_DeprecatedVerificationToken
from prisma.types import HttpConfig, LiteLLM_VerificationTokenInclude
@ -5571,6 +5572,15 @@ def _user_unique_conflict_message(metadata: object, user_id: object) -> str:
return "User conflicts with an existing unique value"
def _raise_user_unique_conflict(error: "UniqueViolationError", user_id: object, *, allow_existing: bool) -> NoReturn:
if allow_existing:
raise error
raise HTTPException(
status_code=409,
detail={"error": _user_unique_conflict_message(error.meta, user_id)},
) from error
class PrismaClient:
spend_log_transactions: list = []
_spend_log_transactions_lock = asyncio.Lock()
@ -6515,10 +6525,7 @@ class PrismaClient:
detail="SSO for more than 5 users requires a LiteLLM Enterprise license. Existing users can still sign in.",
)
except UniqueViolationError as e:
raise HTTPException(
status_code=409,
detail={"error": _user_unique_conflict_message(e.meta, db_data["user_id"])},
) from e
_raise_user_unique_conflict(e, db_data["user_id"], allow_existing=allow_existing_user)
except Exception as e:
if (
"Foreign key constraint failed on the field: `LiteLLM_UserTable_organization_id_fkey (index)`"

View file

@ -221,6 +221,20 @@ async def test_user_conflict_names_constraint_and_does_not_retry(
assert create.await_count == 1
@pytest.mark.asyncio
async def test_user_upsert_recovers_from_concurrent_creation(
prisma_client: PrismaClient, monkeypatch: pytest.MonkeyPatch
) -> None:
row: Final = LiteLLM_UserTable(user_id="requested-user")
conflict: Final = UniqueViolationError({"user_facing_error": {"meta": {"target": ["user_id"]}}})
prisma_client.db.litellm_usertable.upsert = AsyncMock(side_effect=[conflict, row])
monkeypatch.setattr("asyncio.sleep", AsyncMock())
result: Final = await prisma_client.insert_data(data={"user_id": row.user_id}, table_name="user")
assert result is row
@pytest.mark.asyncio
async def test_user_insert_still_retries_transient_connection_failure(
prisma_client: PrismaClient, monkeypatch: pytest.MonkeyPatch