Version: headroom-ai 0.39.1
Symptom: /stats → subscription_window shows growing poll_errors and last_error: "fetch returned None"; polled_at goes stale for hours (our dashboard: "allowance reading is 3h 30m old"). The proxy itself is healthy.
Cause: SubscriptionTracker._maybe_poll (subscription/tracker.py) uses self._current_token (Bearer token captured from the last proxied request) and only falls back to read_cached_oauth_token() when that is empty (token = token or bg_token). Once Claude Code refreshes its OAuth token and writes the new one to ~/.claude/.credentials.json, the in-memory token is rejected with 401. SubscriptionClient.fetch turns this into None, and the tracker never retries with the fresh file token. If no requests flow through the proxy, _current_token is never replaced.
Verified: calling https://api.anthropic.com/api/oauth/usage with the credentials-file token returned 200 while the poller kept failing.
Suggested fix: prefer the credentials-file token (or retry with it after a 401/None), falling back to the in-memory token; optionally clear _current_token on 401. Our local patch:
file_token = read_cached_oauth_token()
snapshot = None
if file_token:
snapshot = await self._client.fetch(file_token)
if snapshot is None and token and token != file_token:
snapshot = await self._client.fetch(token)
After this, poll_errors stayed at 0 following a restart.
Version: headroom-ai 0.39.1
Symptom:
/stats→subscription_windowshows growingpoll_errorsandlast_error: "fetch returned None";polled_atgoes stale for hours (our dashboard: "allowance reading is 3h 30m old"). The proxy itself is healthy.Cause:
SubscriptionTracker._maybe_poll(subscription/tracker.py) usesself._current_token(Bearer token captured from the last proxied request) and only falls back toread_cached_oauth_token()when that is empty (token = token or bg_token). Once Claude Code refreshes its OAuth token and writes the new one to~/.claude/.credentials.json, the in-memory token is rejected with 401.SubscriptionClient.fetchturns this intoNone, and the tracker never retries with the fresh file token. If no requests flow through the proxy,_current_tokenis never replaced.Verified: calling
https://api.anthropic.com/api/oauth/usagewith the credentials-file token returned 200 while the poller kept failing.Suggested fix: prefer the credentials-file token (or retry with it after a 401/None), falling back to the in-memory token; optionally clear
_current_tokenon 401. Our local patch:After this, poll_errors stayed at 0 following a restart.