diff --git a/backend/app/services/provider_usage.py b/backend/app/services/provider_usage.py index 57f54ba..76f3e0e 100644 --- a/backend/app/services/provider_usage.py +++ b/backend/app/services/provider_usage.py @@ -951,6 +951,10 @@ async def fetch_provider_usage( if cached is not None: return cached + # Tracks whether a subscription-window fetch was attempted so we can skip + # caching on transient empty-window failures (avoids stale navbar state). + _subscription_attempted = False + if provider == "anthropic": # Auto-detected local OAuth token (sk-ant-oat01-) takes precedence — it is # the correct credential type for the oauth/usage endpoint and is always @@ -974,6 +978,7 @@ async def fetch_provider_usage( ) # Overlay subscription windows from OAuth token (auto or explicit) if effective_session_key and result.reachable is not False: + _subscription_attempted = True sub_windows = await _fetch_anthropic_subscription(effective_session_key) if sub_windows: result.subscription_windows = sub_windows @@ -1001,6 +1006,7 @@ async def fetch_provider_usage( local_codex = _read_codex_local_token() effective_codex_key = session_key or local_codex if effective_codex_key and result.reachable is not False: + _subscription_attempted = True sub_windows, plan_label = await _fetch_codex_subscription(effective_codex_key) if sub_windows: result.subscription_windows = sub_windows @@ -1020,7 +1026,16 @@ async def fetch_provider_usage( ) result.account_key = account_key - _set_cached(credential_id, result) + # Skip caching when subscription windows were expected but came back empty — + # a transient failure at startup would otherwise be served stale for up to + # CACHE_TTL_SECONDS, causing the navbar to show no usage data. + if _subscription_attempted and not result.subscription_windows: + logger.debug( + "provider_usage.skip_cache provider=%s reason=empty_subscription_windows", + provider, + ) + else: + _set_cached(credential_id, result) logger.info( "provider_usage.checked provider=%s account=%s reachable=%s error=%s", provider, account_key, result.reachable, result.error,