Skip to content

fix(codex): preserve pool reauthentication failure causes - #5191

Closed
luvs01 wants to merge 2 commits into
lidge-jun:devfrom
luvs01:fix/pool-reauth-failure-cause
Closed

luvs01 wants to merge 2 commits into
lidge-jun:devfrom
luvs01:fix/pool-reauth-failure-cause

Conversation

@luvs01

@luvs01 luvs01 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Motivation

poolAccountDto collapses three independent reauthentication causes into one needsReauth boolean and then guesses the reason from overlapping booleans. When the pool quota probe observed a terminal token-refresh failure, the DTO reported quota_unauthorized, and the health projection always claimed refresh_failed — so a WHAM 401 and a dead refresh grant were indistinguishable to the operator who has to act on them.

Description

  • PoolQuotaResult now carries reauthReason ("refresh_failed" | "quota_unauthorized"), recorded where the probe observes the failure: terminal WHAM 401s on the initial response and the replay report quota_unauthorized; terminal TokenRefreshErrors and terminal-fenced refresh lineages report refresh_failed.
  • poolAccountDto prefers the observed quotaResult.reauthReason over boolean inference, and maps it into projectCodexAccountHealth so the persisted verdict reports unauthorized for quota/missing-credential failures instead of always refresh_failed.
  • Two regression tests cover the WHAM-401 and terminal-refresh paths through handleCodexAuthAPI.

Tests

  • bun test tests/codex-integration/codex-auth-api.test.ts tests/usage/quota-401-recovery-runtime.test.ts tests/oauth/oauth-health.test.ts — 374 pass, 0 fail
  • bun x tsc --noEmit — clean

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Account health responses now include a more specific reauthentication reason, distinguishing quota authorization failures from token refresh failures.
    • Reauthentication status is consistently reflected across account listing and refresh responses.
  • Tests

    • Added coverage for quota authorization failures and failed OAuth token refresh scenarios.

PoolQuotaResult now carries the failure source observed while obtaining WHAM/quota or refreshing tokens, so poolAccountDto reports the actual cause instead of inferring it from overlapping booleans. Terminal WHAM 401s surface as quota_unauthorized and terminal refresh failures as refresh_failed.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@github-actions github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Sep 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/oauth/health.ts.

@github-actions github-actions Bot added the bug Something isn't working label Sep 19, 2026
@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • hygiene: unsponsored_surface.

What to do

  • Fix unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/oauth/health.ts.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.

@github-actions
github-actions Bot marked this pull request as draft September 19, 2026 17:04
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 128c09b2-2730-431f-849e-be4fdfd573c8

📥 Commits

Reviewing files that changed from the base of the PR and between efb55e7 and a063fd0.

📒 Files selected for processing (4)
  • src/codex/auth-api/account-list.ts
  • src/codex/auth-api/pool-quota-probe.ts
  • src/oauth/health.ts
  • tests/codex-integration/codex-auth-api.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The change records reauthentication failure sources in pool quota results, passes normalized reasons through account health projection, and exposes the resulting reason in account DTOs. Integration tests cover quota authorization and OAuth refresh failures.

Changes

Reauthentication reason propagation

Layer / File(s) Summary
Record quota reauthentication sources
src/codex/auth-api/pool-quota-probe.ts
PoolQuotaResult adds reauthReason. Unauthorized terminal paths return quota_unauthorized. Token refresh failure paths return refresh_failed at lines 183–231, 263, and 406.
Project health and account DTO reasons
src/oauth/health.ts, src/codex/auth-api/account-list.ts
projectCodexAccountHealth accepts an optional reason and forwards it to OAuth health projection. poolAccountDto prioritizes quotaResult.reauthReason, normalizes the reason for health, and uses the health reason as a fallback for reauth_required responses at lines 108–127.
Validate API reauthentication reasons
tests/codex-integration/codex-auth-api.test.ts
Integration tests at lines 2093–2129 verify quota_unauthorized for a 401 quota response and refresh_failed for an OAuth invalid_grant response.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AccountsAPI
  participant poolAccountDto
  participant PoolQuotaProbe
  participant OAuthTokenEndpoint
  Client->>AccountsAPI: request account data
  AccountsAPI->>poolAccountDto: build account DTO
  poolAccountDto->>PoolQuotaProbe: obtain quota result
  PoolQuotaProbe->>OAuthTokenEndpoint: refresh token when required
  OAuthTokenEndpoint-->>PoolQuotaProbe: return refresh result
  PoolQuotaProbe-->>poolAccountDto: return reauthReason
  poolAccountDto-->>AccountsAPI: return account DTO reason
  AccountsAPI-->>Client: return reauthReason
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: preserving the causes of pool reauthentication failures. This matches the updates to PoolQuotaResult, poolAccountDto, health projection, and regression…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 46 / 80

이 PR은 풀 계정이 다시 로그인이 필요할 때, 그 이유를 응답에 적습니다. 예전 목록은 원인 세 개를 참/거짓 하나로 합친 뒤, 겹친 조건으로 이유를 추측했습니다. 사용량 조회가 401로 끝나도, 갱신 토큰이 거절돼도, 건강 상태는 늘 갱신 실패였습니다. 목록을 보는 사람은 다시 로그인해야 하는지, 사용 권한만 없는지 알 수 없었습니다.

이제는 조회가 실패한 자리에 이유를 남깁니다. 사용량 주소가 끝까지 401이면 quota_unauthorized입니다. 갱신 토큰이 거절되면 refresh_failed입니다. 계정 목록은 이 값을 추측보다 먼저 씁니다. 건강 상태에는 할당량 거절과 자격 증명 없음을 unauthorized로, 갱신 실패를 refresh_failed로 넘깁니다. 테스트 두 개가 그 두 응답을 확인합니다. 베이스는 dev이고, 같은 주제의 다른 열린 PR은 없습니다. PR은 아직 초안입니다. src/oauth/health.ts가 인증 표면이라 검사에서 unsponsored_surface로 막혀 있습니다.

src/codex/auth-api/pool-quota-probe.ts:404 - fetchFreshPoolAccountQuotaTokenRefreshError이면 이유와 상관없이 needsReauth: truerefresh_failed를 돌려줍니다. 같은 파일 229줄의 401 복구는 revokedexpired만 되돌릴 수 없는 실패로 봅니다. unknown은 토큰 서버가 5xx를 준 경우이고, 계정 저장소는 이걸 일시 실패로 둡니다. 이번 이름은 그 일시 실패를 갱신 토큰이 죽은 것처럼 보여 줍니다. PR 설명은 되돌릴 수 없는 갱신 실패만 적는다고 했는데, 이 함수에는 그 필터가 없습니다. 진짜로 죽은 invalid_grant도 여기서는 markAccountNeedsReauth를 부르지 않습니다. 230줄 복구는 표시를 남깁니다. 그래서 죽은 갱신 토큰은 이번 응답에만 이유가 보이고, 캐시가 남은 다음 목록은 다시 건강으로 나옵니다.

src/oauth/health.ts:216 - 저장된 검증이 401이나 403으로 실패하면 재인증이 필요하다고 올립니다. 243줄은 호출자가 이유를 안 주면 무조건 refresh_failed입니다. 조회 자체는 괜찮은데 검증 기록만 401인 계정은, 이번 수정 뒤에도 건강 상태가 갱신 실패입니다. 403이 갈 forbidden으로 이어지는 길도 없습니다.

src/codex/auth-api/account-list.ts:85 - 타입 주석은 projectCodexAccountHealth가 아직 refresh_failed만 낸다고 적혀 있습니다. 이 PR이 unauthorized를 넘기게 바꿔서, 그 문장은 지금 코드와 다릅니다.

tests/codex-integration/codex-auth-api.test.ts:2093 - 두 테스트는 계정 JSON의 reauthReason만 봅니다. 건강 상태의 reasonunauthorized인지, unknown 갱신 오류가 재인증으로 안 올라가는지는 확인하지 않습니다. 건강 쪽 매핑이 되돌아가도 테스트는 통과합니다.

메인테이너의 판단이 필요한 지점

TokenRefreshError를 전부 재인증으로 보는 것은 이번 PR 이전부터입니다. 이번 변경은 그 위에 refresh_failed라는 이름을 붙입니다. 5xx를 그 한 번 응답에서만 재인증으로 보여줄지, needsReauth를 끄고 다음 조회에 맡길지는 정해 주세요. 저장소의 isTerminalCodexPoolRefreshFailure는 이미 다음 조회에 맡기는 쪽입니다.

저장된 401/403 검증 실패를 이 PR에서 unauthorizedforbidden으로 나눌지, 조회가 본 원인만 고치고 검증 기록은 다음으로 둘지도 정해 주세요.

너의 추천

404줄은 isTerminalRefreshError가 참일 때만 refresh_failed를 남기세요. unknown은 401 복구와 같이 needsReauth: false로 두세요. 죽은 갱신(revoked, expired)은 230줄처럼 markAccountNeedsReauth를 호출해서, 다음 목록에서도 같은 이유가 보이게 하세요. projectCodexAccountHealth는 저장된 http_status:401unauthorized, http_status:403forbidden으로 올리세요. 테스트에 그 경우와 건강 상태 reason을 같이 넣으세요. 85줄 주석은 지금 동작에 맞게 고치세요. 인증 파일 검사가 막혀 있으니 maintainer-sponsored 전에는 머지하지 않는 편이 좋습니다.

이 댓글은 grok-bot이 작성했습니다

lidge-jun pushed a commit that referenced this pull request Sep 19, 2026
PoolQuotaResult now carries the failure source observed while obtaining
WHAM/quota or refreshing tokens, so poolAccountDto reports the actual
cause instead of inferring it from overlapping booleans. Terminal WHAM
401s surface as quota_unauthorized and terminal refresh failures as
refresh_failed.

Carried from #5191. The two regression cases are registered from
tests/helpers/pool-reauth-cause.ts rather than written inline: the
original placement grew tests/codex-integration/codex-auth-api.test.ts
from 6522 to 6560 lines against a 6549-line file-size-baseline cap that
only ever moves downward. Registering them through the same
registerXCases seam the file already uses for two other helpers leaves
it at 6525 with the assertions byte-identical.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@luvs01

luvs01 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the review findings in 8ba026a (pushed to fix/pool-reauth-failure-cause):

  • pool-quota-probe.ts: fetchFreshPoolAccountQuota now emits refresh_failed only for terminal refresh failures via the account store''s isTerminalCodexPoolRefreshFailure (revoked/expired grant or missing credential). Transient/unknown refresh errors (token-endpoint 5xx, transport) return needsReauth: false like the 401-recovery path, and terminal failures now call markAccountNeedsReauth so the cause persists on the next listing instead of flipping back to healthy once cached.
  • oauth/health.ts: projectCodexAccountHealth surfaces a stored verification failure''s own cause when the caller gives no reason - http_status:401 -> unauthorized, http_status:403 -> forbidden.
  • account-list.ts: updated the stale CodexAccountReauthReason comment to match the widened projection.
  • Tests: the quota-401 test now asserts health.reason = unauthorized; the refresh-rejection test asserts the dead grant stays marked on a cached listing; a new test covers a transient server_error refresh producing no needsReauth/refresh_failed; projectCodexAccountHealth gains stored http_status:401/403 -> unauthorized/forbidden cases; the deferred-validation matrix now expects the stored reason after volatile state is cleared.

Verified: bun test tests/codex-integration/codex-auth-api.test.ts (343 pass), bun test tests/oauth/oauth-health.test.ts (24 pass), bun x tsc --noEmit clean.

lidge-jun added a commit that referenced this pull request Sep 19, 2026
…ice (#5199)

* fix(codex): preserve pool reauthentication failure causes

PoolQuotaResult now carries the failure source observed while obtaining
WHAM/quota or refreshing tokens, so poolAccountDto reports the actual
cause instead of inferring it from overlapping booleans. Terminal WHAM
401s surface as quota_unauthorized and terminal refresh failures as
refresh_failed.

Carried from #5191. The two regression cases are registered from
tests/helpers/pool-reauth-cause.ts rather than written inline: the
original placement grew tests/codex-integration/codex-auth-api.test.ts
from 6522 to 6560 lines against a 6549-line file-size-baseline cap that
only ever moves downward. Registering them through the same
registerXCases seam the file already uses for two other helpers leaves
it at 6525 with the assertions byte-identical.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(server): stop a local catalog-sync warning from un-readying the proxy

/readyz went to a terminal failed state on any nonempty warning from the
post-startup Codex sync. A warning there describes artifacts written into
the LOCAL Codex home, not the proxy's ability to serve: no catalog source
so Codex keeps its native catalog, combos omitted from the catalog, a
conversation-history relabel left to Codex's own writer, or a caught
catalog-refresh exception after which config injection still runs and
still reports its own ok. The sync continues past every one of them.

#5181 is what that cost. A single-replica Kubernetes deployment lost its
only Service endpoint while every non-Codex route stayed healthy, because
the readiness probe read a degradation of a local file as a dead process.
Switching the probe to /healthz restored service, which is the reporter's
own evidence that the process was viable.

ok and warning are separate fields in the sync result because they answer
separate questions. ok is the sync's verdict on the essential work - the
write admission and the config injection. The gate now follows ok alone.

The narrowing is only to warning. A throw, a null outcome, and ok !== true
stay terminal, so the service-home write refusal, the external-provider
injection failure, the config/integrity preflight refusal and the final
injection failure all still fail readiness. A throw and a null stay
terminal for a different reason: neither is a classified outcome, so the
gate cannot claim one. Readiness also still stays pending until the sync
SETTLES, which is the race the gate was built for and which this does not
touch.

This is the boundary the Claude Code roster reconciliation already has -
it delays the ready transition without being allowed to fail it - applied
to the catalog sync.

Scope note: the brief for this work held that the Codex pool's
"needs reauthentication" warning was itself the terminal warning. It is
not. That line comes from warnGatedNativeSuppressedOnce
(src/codex/catalog/gated-native-warn.ts) and is a bare console.warn that
never enters the structured result, and per-account pool refresh failures
are caught and returned as null by model-entitlements.ts rather than
propagating. The account-level fault that does reach the gate is a native
MAIN credential refresh failing outside that per-account catch: it rejects
the entitlement gather, surfaces as the catch-all "catalog sync skipped"
warning, and permanently un-readies the process. That is the same class of
blast radius the report describes, and it is closed here.

Closes #5181

* fix(codex): scope model denial evidence to the credential generation

An authenticated unsupported-model refusal is remembered for six hours
under (account id, model id) with no credential generation. Re-auth of
the same pool account keeps the internal id and increments the
generation, and can swap the subscription underneath it — so the
previous credential's refusal keeps steering routing away from an
account that now has access.

The account-wide forget that would have covered this
(`model-entitlements.ts`) sits behind a condition requiring a
previously cached roster with changed identity, so with no roster it
never runs — which is exactly the state the flagship request path is
usually in, since nothing on it refills the five-minute roster cache.

Entries now carry the generation they were observed under, and:

- the reader drops evidence whose generation is no longer live, so
  superseded evidence stops influencing the replacement without
  depending on the conditional forget;
- a write from an older generation cannot overwrite newer evidence, so
  an in-flight generation-G refusal landing after G+1 is saved neither
  resurrects nor rewrites it;
- a clear from an older generation cannot delete newer evidence, so a
  late generation-G success cannot re-admit an account the current
  credential has just been refused by. An equal generation still
  clears: that is the ordinary "this account just served this model"
  case.

Evidence that cannot name a generation is dropped rather than
attributed to whatever is current. Main-account contexts carry no pool
generation, and already carry no account id, so nothing changes there.

Cache-only routing, the six-hour TTL, the entry bound, positive-roster
precedence and empty-filter restoration are all untouched.

The liveness predicate is injected rather than imported so the store
stays a leaf module; production wires `isCodexAccountGenerationLive`.

Closes #4952

* fix(codex): keep main-pool denial evidence and fence stale writes before they evict

Three defects in the carried #5092, found by adversarial review of the
generation fences rather than by CI, which was green on all three.

1. main-pool evidence was silently discarded. Both production call sites
   pass `kind === "pool" ? generation : undefined`, and the carried code
   dropped any refusal that could not name a generation. A `main-pool`
   context - the stored main login taking part in rotation - has a real
   accountId and no POOL credential generation, because its credential
   lives in auth.json. So for that account #4906 reverted: the pool would
   re-send the model the login had just refused, on every request. A
   refusal with no generation is now ACCOUNT-scoped and the generation
   fences do not apply to it. This is the rule core-codex-account.ts
   already uses for the quota writer, where an absent credential
   generation skips the liveness check instead of discarding the write.
   Request-owned `main` is unaffected: its accountId is null and the
   existing guard returns before any of this.

2. The stale-write fence only rejected a late refusal when the same key
   already held newer evidence. With no entry for its own key the stale
   row was inserted, and at the 512-entry bound that insert evicts the
   oldest valid row - which no later read fence can restore, because the
   evidence is gone. The write now rejects a generation that is no longer
   live before touching the map at all, which closes the race the issue
   names rather than only its common case.

3. The read fence validated liveness for every non-expired entry before
   checking the model or the caller's exclusion set, and DELETED any row
   that came back not-live. Two problems. The issue asks for validation
   after the exclusion read fence, and an excluded account - a draining
   profile switch, or a request-owned credential - was causing a
   credential-store read on its behalf. And the predicate cannot tell
   "this account reauthenticated" from "the store could not be read":
   loadCodexAccountRecordStore catches a read failure and returns {}, so
   an unreadable store deleted valid evidence permanently. The reader now
   matches the model and applies the exclusion set first, and SKIPS a
   non-live row instead of deleting it. Skipping already produces the
   routing outcome on every read; deletion only saved memory in a bounded
   map, and it was the part that was unsafe. Superseded rows still leave
   by TTL, by eviction, and by the account-wide forget.

Each liveness check also reloaded and reparsed the whole credential store
per row, on the request path. The seam is now a factory that opens once
per lookup over one loadCodexAccountRecordSnapshot, which is the shape
that snapshot was added for, and it is not opened at all when no matching
row carries a generation.

Every constraint the issue sets is unchanged: cache-only routing with no
added upstream fetch, the six-hour TTL, the 512-entry bound,
positive-roster precedence, and empty-filter restoration. This remains an
ordering preference, not an eligibility filter, and a live-generation
refusal still denies.

Co-authored-by: Abhishek Sharma <abhicse24@gmail.com>

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
Co-authored-by: Abhishek Sharma <abhicse24@gmail.com>
@lidge-jun

Copy link
Copy Markdown
Owner

추가 리뷰 · 우선순위 32 / 80

8ba026a는 지난 리뷰에서 말한 것 대부분을 고쳤습니다. 토큰 서버가 5xx를 주면 이제 재로그인을 요구하지 않습니다. 갱신 토큰이 거절되거나 만료된 경우만 refresh_failed로 적고, 그 계정을 다시 로그인 필요로 표시합니다. 이 프로세스가 켜져 있는 동안은, 사용량이 캐시된 다음 목록에서도 그 이유가 보입니다. 파일에 남은 검증이 401이면 unauthorized, 403이면 forbidden이 됩니다. 주석도 지금 동작과 맞습니다. 베이스는 dev이고, PR은 아직 초안입니다. src/oauth/health.ts가 인증 파일이라 unsponsored_surface는 그대로 막혀 있습니다.

src/codex/auth-api/account-list.ts:116 - 메모리에 재로그인 표시가 있으면 건강 이유로 무조건 refresh_failed를 넘깁니다. src/oauth/health.ts:253은 호출자가 이유를 주면, 파일에 적힌 401/403보다 그 이유를 먼저 씁니다. 검증이 401로 실패한 계정도, 프로세스가 표시를 기억하는 동안은 건강 상태가 갱신 실패입니다. unauthorizedforbidden은 그 표시를 지운 뒤에만 나옵니다. tests/codex-integration/codex-auth-api.test.ts:5427이 그 순서를 정답으로 잠가 두었습니다.

src/codex/auth-api/pool-quota-probe.ts:410 - markAccountNeedsReauth는 이 프로세스의 메모리입니다. 디스크의 검증 기록은 쓰지 않습니다. 서버를 다시 켜면 표시가 사라지고, 사용량 캐시가 남은 목록은 갱신을 건너뜁니다. 죽은 갱신 토큰이 다시 건강으로 보입니다. 같은 프로세스 안의 테스트는 통과하지만, 재시작은 보지 않습니다. 406줄 주석은 다음 목록에 이유가 남는다고 하는데, 그건 프로세스가 살아 있는 동안만 맞습니다.

메인테이너의 판단이 필요한 지점

검증 401/403이 메모리 표시보다 먼저여야 하는지 정해 주세요. 작성자는 표시가 있는 동안은 refresh_failed, 지운 뒤에는 저장된 상태 코드를 보여주는 쪽을 골랐습니다.

죽은 갱신을 이 조회에서도 디스크에 남길지 정해 주세요. 토큰 가디언은 이미 refresh_revokedrefresh_expired를 터미널로 저장합니다. 이 함수는 메모리 표시만 합니다.

너의 추천

116줄은 메모리 표시만 이유일 때 이유를 넘기지 마세요. 그러면 253줄이 저장된 401과 403을 그대로 씁니다. 410줄에서 갱신이 revokedexpired로 끝나면, 가디언처럼 markCodexAccountValidationFailedterminal: true를 주세요. 재시작 뒤에도 같은 이유가 남습니다. maintainer-sponsored 전에는 머지하지 마세요.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun

Copy link
Copy Markdown
Owner

The change from this PR landed on dev as part of #5199, squash-merged as 09698b9b3a, with a Co-authored-by trailer naming you.

It went in alongside the /readyz scoping fix for #5181, and the pairing is deliberate: that fix stops a degraded Codex pool from taking the whole proxy out of service, and yours is what makes the remaining degradation actionable. Reporting the observed cause instead of guessing from overlapping booleans is the difference between an operator who knows whether to re-authenticate or to repair a dead refresh grant, and one who only knows something is wrong.

Closing because the content is on dev. Thank you for the fix.

@luvs01

luvs01 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator Author

A note for the record: the #5199 squash (09698b9) carried this PR's original commit only — the review-fix commit pushed to this branch before the squash, and the follow-up scope recommended in the second review, did not land on dev. Opened #5222 as a follow-up carrying both, with the two reviewer-flagged decision points implemented per the reviewer's recommendation.

@luvs01
luvs01 deleted the fix/pool-reauth-failure-cause branch September 20, 2026 04:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants