Skip to content

Commit 70ccb3e

Browse files
samxu01claude
andcommitted
fix(litellm): fail fast on dead codex auth instead of blocking startup
When auth.json is expired and the refresh token is dead, litellm's chatgpt authenticator (get_access_token) drops into _login_device_code(), which prints a device code and BLOCKS up to 15 min polling for an operator to approve. The litellm pod is headless, so startup hangs past the startup probe -> SIGKILL -> crash loop, and each restart re-requests a device code, hammering the endpoint into a 429. This turned a routine ~2-week ChatGPT token expiry into a ~10-day litellm OUTAGE (2026-06-24): the proxy never bound :4000, so the router's existing codex->nemotron fallback never got a turn (fallbacks only apply to in-flight requests on a running proxy). Fix: a startup monkeypatch (matching the existing responses-input / tool-index patches) makes get_access_token() raise GetAccessTokenError instead of the interactive device-login when CHATGPT_DISABLE_DEVICE_LOGIN is set (now set on the litellm container). Dead auth -> litellm boots, marks codex unhealthy, and the router falls back to nemotron until an operator re-auths OUT OF BAND via the codex-cli sidecar (a separate codex process, unaffected by this patch). Idempotent; no-ops if the upstream signature changes. Validated: patch matches exactly once + py_compile-clean against the deployed authenticator.py. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0d0f681 commit 70ccb3e

1 file changed

Lines changed: 36 additions & 0 deletions

File tree

k8s/helm/commonly/templates/agents/litellm-deployment.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,34 @@ spec:
168168
proxy_handler_instance = RateLimitSignaler()
169169
PYEOF
170170
echo "rate-limit-signaler callback installed at /app/rate_limit_signal.py"
171+
# Patch litellm's ChatGPT authenticator to FAIL FAST in headless mode instead
172+
# of dropping into an interactive device-login. When auth.json is expired AND
173+
# the refresh token is dead, the stock get_access_token() calls
174+
# _login_device_code(), which prints a device code and BLOCKS up to 15 min
175+
# polling for an operator to approve. There is no operator in this pod, so it
176+
# hangs past the startup probe -> SIGKILL -> crash loop, and each restart
177+
# re-requests a device code, hammering the endpoint into a 429. This turned a
178+
# routine ~2-week token expiry into a ~10-day litellm OUTAGE (2026-06-24): the
179+
# proxy never bound :4000, so the router's codex->nemotron fallback never got a
180+
# turn. With CHATGPT_DISABLE_DEVICE_LOGIN set, get_access_token() raises
181+
# GetAccessTokenError instead -> litellm boots, marks codex unhealthy, and the
182+
# fallback serves requests until an operator re-auths OUT OF BAND via the
183+
# codex-cli sidecar (a separate codex process, unaffected by this patch).
184+
# Idempotent; no-ops cleanly if the upstream signature changes.
185+
python3 -c "
186+
import litellm, os
187+
f=os.path.join(os.path.dirname(litellm.__file__),'llms','chatgpt','authenticator.py')
188+
s=open(f).read()
189+
old=' cooldown_remaining = self._get_device_code_cooldown_remaining(auth_data)'
190+
new=' if os.getenv(\"CHATGPT_DISABLE_DEVICE_LOGIN\"): # commonly: fail-fast headless\n raise GetAccessTokenError(\"ChatGPT auth invalid/expired and interactive device-login is disabled (CHATGPT_DISABLE_DEVICE_LOGIN); re-auth out-of-band via the codex-cli sidecar. Requests fall back per router fallbacks.\")\n' + old
191+
if 'commonly: fail-fast headless' in s:
192+
print('LiteLLM chatgpt fail-fast patch: already applied')
193+
elif old in s:
194+
open(f,'w').write(s.replace(old,new,1))
195+
print('LiteLLM chatgpt fail-fast patch applied: headless device-login -> GetAccessTokenError')
196+
else:
197+
print('LiteLLM chatgpt fail-fast patch: pattern not found (version changed?) - review needed')
198+
"
171199
exec litellm --config /app/config.yaml
172200
ports:
173201
- name: http
@@ -221,6 +249,14 @@ spec:
221249
# sidecar). See ADR-014.
222250
- name: CHATGPT_TOKEN_DIR
223251
value: /chatgpt-auth
252+
# Fail fast instead of interactive device-login when codex auth is dead. The
253+
# chatgpt authenticator otherwise blocks startup on a 15-min device-code poll
254+
# (no operator in-pod) -> crash loop -> 429 endpoint lockout. With this set, dead
255+
# auth raises GetAccessTokenError and the router falls back to nemotron; an
256+
# operator re-auths out-of-band via the codex-cli sidecar. See the startup patch
257+
# above + ADR-014.
258+
- name: CHATGPT_DISABLE_DEVICE_LOGIN
259+
value: "1"
224260
# Force prompt/response storage in spend logs regardless of runtime general_settings.
225261
# The config-file setting (store_prompts_in_spend_logs: true) is sometimes shadowed by
226262
# the in-memory general_settings dict at startup; the env var is the reliable fallback.

0 commit comments

Comments
 (0)