What
getUsageLimits() can hang forever before it reaches the provider timeout layer. When Codex auth is stale, src/lib/usage-limits.js awaits refreshCodexTokens() in the serial prelude and passes the raw fetchImpl. src/lib/codex-token-refresh.js:20-27 performs that request without an AbortSignal or timeout.
PR #143 adds single-flight coalescing after the completed-cache check. That improves fan-out efficiency, but it changes the blast radius of this existing hang: every later quota request joins the same unsettled sweep instead of starting an independent attempt.
Evidence
Impact
A stalled Codex OAuth endpoint can make the usage-limits endpoint remain pending indefinitely. With #143, one stalled refresh holds the single-flight slot and all dashboard tabs, scheduled revalidations, and manual quota refreshes join it.
Suggested shape
- Give
refreshCodexTokens a bounded timeout/abort path, preferably using the same configured provider timeout contract rather than a second unrelated constant.
- Ensure the fetch is actually aborted where supported, not merely raced while the underlying request continues.
- Preserve existing refresh error semantics: best-effort failure falls back to the current access token;
REFRESH_TOKEN_EXPIRED remains actionable.
Definition of done
Scope
Do not redesign all provider scheduling in this issue. This is the unbounded serial Codex refresh prelude only.
What
getUsageLimits()can hang forever before it reaches the provider timeout layer. When Codex auth is stale,src/lib/usage-limits.jsawaitsrefreshCodexTokens()in the serial prelude and passes the rawfetchImpl.src/lib/codex-token-refresh.js:20-27performs that request without anAbortSignalor timeout.PR #143 adds single-flight coalescing after the completed-cache check. That improves fan-out efficiency, but it changes the blast radius of this existing hang: every later quota request joins the same unsettled sweep instead of starting an independent attempt.
Evidence
src/lib/codex-token-refresh.js:20-27: rawfetchImpl()call with no timeout/abort.src/lib/usage-limits.js:1954-1959on PR feat(limits): coalesce concurrent quota provider fan-outs #143: serialawait refreshCodexTokens(...)beforeproviderFetch = withFetchTimeout(...)is created at:1981.Impact
A stalled Codex OAuth endpoint can make the usage-limits endpoint remain pending indefinitely. With #143, one stalled refresh holds the single-flight slot and all dashboard tabs, scheduled revalidations, and manual quota refreshes join it.
Suggested shape
refreshCodexTokensa bounded timeout/abort path, preferably using the same configured provider timeout contract rather than a second unrelated constant.REFRESH_TOKEN_EXPIREDremains actionable.Definition of done
fetchImplcontrol that fails without the fix.npm run ci:localpasses.Scope
Do not redesign all provider scheduling in this issue. This is the unbounded serial Codex refresh prelude only.