BerriAI/litellm · error · HTTPException

str(e)

Error message

str(e)

What it means

bulk_update_processed_users loops users calling the single-user update and records per-item outcomes (successful_updates / failed_updates) in its results array; the outer except is a last resort. Any exception raised outside the per-item loop - a DB connection dropping between items, an error assembling the BulkUpdateUserResponse - becomes HTTP 500 {'error': str(e)}, with the original logged via verbose_proxy_logger.exception('Failed to update users: ...').

Source

Thrown at litellm/proxy/management_endpoints/internal_user_endpoints.py:1662

                results.append(
                    UserUpdateResult(
                        user_id=user_request.user_id,
                        user_email=user_request.user_email,
                        success=False,
                        error=error_message,
                    )
                )
                failed_updates += 1

        return BulkUpdateUserResponse(
            results=results,
            total_requested=len(users_to_update),
            successful_updates=successful_updates,
            failed_updates=failed_updates,
        )
    except Exception as e:
        verbose_proxy_logger.exception("Failed to update users: %s", e)
        raise HTTPException(status_code=500, detail={"error": str(e)})


@router.post(
    "/user/bulk_update",
    tags=["Internal User management"],
    dependencies=[Depends(user_api_key_auth)],
    response_model=BulkUpdateUserResponse,
)
@management_endpoint_wrapper
async def bulk_user_update(
    data: BulkUpdateUserRequest,
    user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
    litellm_changed_by: str | None = Header(
        None,
        description="The litellm-changed-by header enables tracking of actions performed by authorized users on behalf of other users, providing an audit trail for accountability",
    ),
):
    """

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Inspect server logs - the exception and traceback are logged with 'Failed to update users'
  2. Use the per-item results from any partial response to see which users already succeeded before re-running
  3. If the DB dropped, restore connectivity first (check DATABASE_URL / Postgres health), then retry the remaining users
  4. Chunk large batches so a structural 500 costs less rework
Defensive patterns

Strategy: retry

Validate before calling

import requests

def db_ready(base_url: str, admin_key: str) -> bool:
    r = requests.get(f"{base_url}/health/liveliness",
                     headers={"Authorization": f"Bearer {admin_key}"}, timeout=10)
    return r.ok and r.json().get("litellm_database", "") != ""

Try / catch

except requests.HTTPError as e:
    if e.response is not None and e.response.status_code == 500:
        # reconcile state first: re-fetch affected users, then retry only the unfinished ones
        remaining = [u for u in users if not already_applied(u)]
        return retry_bulk(remaining)

Prevention

When it happens

Trigger: A bulk user update where something structural fails mid-run: database disconnect, serialization failure while building the response, or an exception escaping a helper outside the per-item try block in POST /user/bulk_update processing.

Common situations: Large bulk jobs hitting transient Postgres drops; monitoring the HTTP status instead of per-item result statuses; re-running a whole batch after a 500 without reconciling which users already succeeded.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/fefb606871e81f1f. Report an issue: GitHub.