BerriAI/litellm · error · ProxyException
Customer already exists, passed user_id={data.user_id}. Plea
Error message
Customer already exists, passed user_id={data.user_id}. Please pass a new user_id. What it means
LiteLLM_EndUserTable enforces a unique constraint on user_id. When the Prisma create inside POST /customer/new fails with 'Unique constraint failed on the fields: (user_id)', the endpoint maps it to a 400 bad_request ProxyException (type=bad_request, param=user_id) telling you to pass a new user_id.
Source
Thrown at litellm/proxy/management_endpoints/customer_endpoints.py:483
new_end_user_obj.get("object_permission"),
)
new_end_user_obj.pop("object_permission", None)
## WRITE TO DB ##
end_user_record: Final = await _typed_table(EndUserRepository(prisma_client)).create(
data=new_end_user_obj,
include={"litellm_budget_table": True, "object_permission": True},
)
await _evict_end_user_cache_keys(_end_user_cache_keys((data.user_id,)))
return _to_customer_response(end_user_record)
except Exception as e:
verbose_proxy_logger.exception(
"litellm.proxy.management_endpoints.customer_endpoints.new_end_user(): Exception occured - %s", e
)
if "Unique constraint failed on the fields: (`user_id`)" in str(e):
raise ProxyException(
message=f"Customer already exists, passed user_id={data.user_id}. Please pass a new user_id.",
type="bad_request",
code=400,
param="user_id",
)
raise handle_exception_on_proxy(e)
@router.get(
"/customer/info",
tags=["Customer Management"],
dependencies=[Depends(user_api_key_auth)],
response_model=CustomerResponse,
)
@router.get(
"/end_user/info",
tags=["Customer Management"],
include_in_schema=False,View on GitHub (pinned to 77b7c6c40c)
Solutions
- Check first: GET /customer/info?end_user_id=<id> - if it returns 200, update instead of create
- If you meant to modify the existing customer, call /customer/update with the same user_id
- Make creates idempotent: derive user_id deterministically and treat 'already exists' as success
Example fix
# before
create_customer(user_id="cust-42", ...) # 400 on the second run
# after
def ensure_customer(payload):
r = post("/customer/new", payload)
if r.status_code == 400 and "already exists" in r.text:
return get("/customer/info", params={"end_user_id": payload["user_id"]})
r.raise_for_status()
return r Defensive patterns
Strategy: try-catch
Try / catch
import httpx
def create_customer_idempotent(base: str, headers: dict, payload: dict) -> dict:
r = httpx.post(f"{base}/customer/new", json=payload, headers=headers)
if r.status_code == 400 and "already exists" in r.text:
return httpx.get(
f"{base}/customer/info",
params={"end_user_id": payload["user_id"]},
headers=headers,
).json()
r.raise_for_status()
return r.json() Prevention
- Check existence with /customer/info before create when user ids may repeat
- Use deterministic user ids (hashes of stable external ids) so retries converge on one customer
- Treat 400 'already exists' as an idempotent-success signal in provisioning scripts
When it happens
Trigger: POST /customer/new with a user_id that already exists - duplicate submission, retry of a request whose response was lost (client timeout), or a bootstrap/seed script run a second time.
Common situations: Non-idempotent create scripts retried on flaky networks; user_id derived from emails or external CRM ids that collide across teams; re-running environment-seeding payloads in CI or after restore.
Related errors
- Cannot delete file {file_id}. The file is referenced by {cou
- max_budget cannot be negative. Received: {data.max_budget}
- soft_budget cannot be negative. Received: {data.soft_budget}
- soft_budget ({data.soft_budget}) must be strictly lower than
- Model '{m}' not in team's allowed models. Team allowed model
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/4a8b9dc4c7b56f06.
Report an issue: GitHub.