From 4618e28046f40a3a91fe914cec59626fa45693e4 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 19 May 2026 07:44:32 +0000 Subject: [PATCH] fix(mcp): return 409 on user-fields conflict for OAuth credential POST store_user_oauth_credential raises ValueError when the (user, server) row already holds a non-OAuth2 payload, which now includes user-fields blobs. Catch it in the explicit credential-POST endpoint and surface 409 instead of letting management_endpoint_wrapper re-raise as 500, mirroring the existing handling on the BYOK and user-field-values POST endpoints. --- .../mcp_management_endpoints.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/litellm/proxy/management_endpoints/mcp_management_endpoints.py b/litellm/proxy/management_endpoints/mcp_management_endpoints.py index f3c0fecb174..3fd5c1a7f00 100644 --- a/litellm/proxy/management_endpoints/mcp_management_endpoints.py +++ b/litellm/proxy/management_endpoints/mcp_management_endpoints.py @@ -2009,15 +2009,25 @@ if MCP_AVAILABLE: status_code=status.HTTP_400_BAD_REQUEST, detail={"error": "User ID not found in token"}, ) - await store_user_oauth_credential( - prisma_client, - user_id, - server_id, - payload.access_token, - refresh_token=payload.refresh_token, - expires_in=payload.expires_in, - scopes=payload.scopes, - ) + try: + await store_user_oauth_credential( + prisma_client, + user_id, + server_id, + payload.access_token, + refresh_token=payload.refresh_token, + expires_in=payload.expires_in, + scopes=payload.scopes, + ) + except ValueError as e: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail={ + "error": "credential_conflict", + "message": str(e), + "server_id": server_id, + }, + ) # Read back the persisted record so the response reflects the stored # expires_at rather than recomputing it here (which could diverge by # milliseconds or if the storage logic ever adds a grace period).