litellm - ✅(Solved) Fix bug(ui): PriceDataReload polls admin APIs every 30s with expired virtual key, spamming server with ProxyException auth errors [1 pull requests, 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#24349Fetched 2026-04-08 01:13:09
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1subscribed ×1

Error Message

06:21:45 - LiteLLM Proxy:ERROR: auth_exception_handler.py:78 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured -
Requester IP Address: <internal>
litellm.proxy._types.ProxyException
INFO: <ip> - "GET /schedule/model_cost_map_reload/status HTTP/1.1" 400 Bad Request

06:50:39 - LiteLLM Proxy:ERROR: auth_exception_handler.py:78 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured -
Requester IP Address: <internal>
INFO: <ip> - "GET /model/cost_map/source HTTP/1.1" 400 Bad Request
[repeating every ~30s]

Root Cause

In price_data_reload.tsx, the useEffect starts an interval but never checks whether API calls return auth errors:

const interval = setInterval(() => {
  fetchReloadStatus();   // throws on HTTP 400, but catch block doesn't stop the interval
  fetchSourceInfo();
}, 30000);

The catch blocks log the error and set default state but do not clear the interval.

Fix Action

Fix

A fix has been submitted in PR #XXXX. Summary:

  • Extract isAuthError() helper to detect HTTP 400/401 responses
  • Wrap fetchReloadStatus / fetchSourceInfo with useCallback
  • On auth error, call stopPolling() which clears the interval and sets pollingDisabled = true
  • Show an <Alert> banner when polling is paused, prompting re-login
  • useEffect depends on pollingDisabled so it restarts correctly after fresh login

PR fix notes

PR #24350: fix(ui): stop polling on expired/invalid key auth error in PriceDataReload

Description (problem / solution / changelog)

Summary

Fixes #24349

The PriceDataReload component polls two admin endpoints every 30 seconds. When the user's virtual key expires (while the JWT cookie remains valid), every poll triggers ProxyException: Expired Key (HTTP 400), spamming the server with repeated auth errors indefinitely.

Changes:

  • isAuthError() module-level helper detects HTTP 400/401 responses
  • fetchReloadStatus and fetchSourceInfo wrapped with useCallback (satisfies React hooks lint rules)
  • On auth error, stopPolling() clears the interval and sets pollingDisabled = true
  • <Alert> banner shown when polling is paused, prompting the user to re-login
  • useEffect depends on pollingDisabled so polling restarts correctly after fresh login (new accessToken)

Tests

  • Added tests for isAuthError() edge cases (non-Error throws, non-matching status codes)

Manual verification: Deployed to production Kubernetes cluster. Zero ProxyException auth errors from polling after fix — confirmed via pod logs over 30+ minutes.

Checklist

  • Touched file: ui/litellm-dashboard/src/components/price_data_reload.tsx only (no backend changes)
  • No new dependencies added
  • Existing useEffect cleanup (clearInterval on unmount) preserved

Changed files

  • ui/litellm-dashboard/src/components/price_data_reload.tsx (modified, +56/-21)

Code Example

ProxyException: Authentication Error - Expired Key

---

06:21:45 - LiteLLM Proxy:ERROR: auth_exception_handler.py:78 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured -
Requester IP Address: <internal>
litellm.proxy._types.ProxyException
INFO: <ip> - "GET /schedule/model_cost_map_reload/status HTTP/1.1" 400 Bad Request

06:50:39 - LiteLLM Proxy:ERROR: auth_exception_handler.py:78 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured -
Requester IP Address: <internal>
INFO: <ip> - "GET /model/cost_map/source HTTP/1.1" 400 Bad Request
[repeating every ~30s]

---

const interval = setInterval(() => {
  fetchReloadStatus();   // throws on HTTP 400, but catch block doesn't stop the interval
  fetchSourceInfo();
}, 30000);
RAW_BUFFERClick to expand / collapse

Describe the bug

The PriceDataReload component (ui/litellm-dashboard/src/components/price_data_reload.tsx) polls two admin endpoints every 30 seconds via setInterval:

  • GET /schedule/model_cost_map_reload/status
  • GET /model/cost_map/source

It authenticates using accessToken = decoded?.key extracted from the JWT cookie. When a user's virtual key expires, the JWT cookie itself may still be valid (the user stays logged in), but every polling request hits user_api_key_auth which raises:

ProxyException: Authentication Error - Expired Key

This produces continuous HTTP 400 errors in the server logs every 30 seconds, indefinitely, until the user manually refreshes the browser — even though the page appears to function normally.

Reproduction

  1. Open the Admin UI → Models & EndpointsPrice Data Reload tab
  2. Leave the browser tab open with an expired virtual key
  3. Observe repeated LiteLLM Proxy:ERROR auth_exception_handler.py:78 - user_api_key_auth(): Exception occured in pod logs every ~30 seconds

Expected behavior

When the polling requests receive HTTP 400/401 (auth failure), the interval should stop. The UI should show a message prompting the user to re-login rather than silently continuing to spam the server.

Logs

06:21:45 - LiteLLM Proxy:ERROR: auth_exception_handler.py:78 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured -
Requester IP Address: <internal>
litellm.proxy._types.ProxyException
INFO: <ip> - "GET /schedule/model_cost_map_reload/status HTTP/1.1" 400 Bad Request

06:50:39 - LiteLLM Proxy:ERROR: auth_exception_handler.py:78 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured -
Requester IP Address: <internal>
INFO: <ip> - "GET /model/cost_map/source HTTP/1.1" 400 Bad Request
[repeating every ~30s]

Root cause

In price_data_reload.tsx, the useEffect starts an interval but never checks whether API calls return auth errors:

const interval = setInterval(() => {
  fetchReloadStatus();   // throws on HTTP 400, but catch block doesn't stop the interval
  fetchSourceInfo();
}, 30000);

The catch blocks log the error and set default state but do not clear the interval.

Fix

A fix has been submitted in PR #XXXX. Summary:

  • Extract isAuthError() helper to detect HTTP 400/401 responses
  • Wrap fetchReloadStatus / fetchSourceInfo with useCallback
  • On auth error, call stopPolling() which clears the interval and sets pollingDisabled = true
  • Show an <Alert> banner when polling is paused, prompting re-login
  • useEffect depends on pollingDisabled so it restarts correctly after fresh login

LiteLLM Version

Reproducible on current main (c89496f).

Environment

  • Deployment: Kubernetes (LiteLLM Proxy)
  • UI accessed via Admin Dashboard
  • Virtual key with expiry set

extent analysis

Fix Plan

To address the issue of continuous polling requests with expired virtual keys, we need to implement the following steps:

  • Extract a helper function isAuthError() to detect HTTP 400/401 responses
  • Wrap fetchReloadStatus and fetchSourceInfo with useCallback to memoize the functions
  • Implement stopPolling() to clear the interval and set pollingDisabled = true on auth error
  • Display an <Alert> banner when polling is paused, prompting the user to re-login

Example Code

// Extract helper function to detect auth errors
const isAuthError = (error: any) => {
  return error.response && (error.response.status === 400 || error.response.status === 401);
};

// Wrap fetch functions with useCallback
const fetchReloadStatus = useCallback(async () => {
  try {
    const response = await fetch('/schedule/model_cost_map_reload/status');
    if (response.ok) {
      // Handle successful response
    } else if (isAuthError(response)) {
      stopPolling();
    }
  } catch (error) {
    if (isAuthError(error)) {
      stopPolling();
    }
  }
}, [stopPolling]);

const fetchSourceInfo = useCallback(async () => {
  try {
    const response = await fetch('/model/cost_map/source');
    if (response.ok) {
      // Handle successful response
    } else if (isAuthError(response)) {
      stopPolling();
    }
  } catch (error) {
    if (isAuthError(error)) {
      stopPolling();
    }
  }
}, [stopPolling]);

// Implement stopPolling function
const stopPolling = () => {
  clearInterval(interval);
  setPollingDisabled(true);
};

// Display Alert banner when polling is paused
if (pollingDisabled) {
  return <Alert banner="true">Polling paused due to auth error. Please re-login to resume.</Alert>;
}

// useEffect depends on pollingDisabled to restart polling after fresh login
useEffect(() => {
  if (!pollingDisabled) {
    const interval = setInterval(() => {
      fetchReloadStatus();
      fetchSourceInfo();
    }, 30000);
    return () => clearInterval(interval);
  }
}, [pollingDisabled, fetchReloadStatus, fetchSourceInfo]);

Verification

To verify the fix, follow these steps:

  • Open the Admin UI and navigate to the Models & EndpointsPrice Data Reload tab
  • Leave the browser tab open with an expired virtual key
  • Observe that the polling requests stop and an <Alert> banner is displayed prompting the user to re-login
  • Re-login and verify that polling resumes correctly

Extra Tips

  • Make sure to handle errors properly and display user-friendly error messages
  • Consider implementing a retry mechanism for failed requests
  • Use a more robust

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…

FAQ

Expected behavior

When the polling requests receive HTTP 400/401 (auth failure), the interval should stop. The UI should show a message prompting the user to re-login rather than silently continuing to spam the server.

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 - ✅(Solved) Fix bug(ui): PriceDataReload polls admin APIs every 30s with expired virtual key, spamming server with ProxyException auth errors [1 pull requests, 1 participants]