Skip to content

fix: retry Codex refresh on token invalidation#4088

Open
kilhyeonjun wants to merge 1 commit into
router-for-me:devfrom
kilhyeonjun:fix/codex-token-invalidated-refresh-retry
Open

fix: retry Codex refresh on token invalidation#4088
kilhyeonjun wants to merge 1 commit into
router-for-me:devfrom
kilhyeonjun:fix/codex-token-invalidated-refresh-retry

Conversation

@kilhyeonjun

Copy link
Copy Markdown

Summary

  • detect Codex 401 token_invalidated responses
  • call the existing CodexExecutor.Refresh() path before suspending/falling back
  • retry the same request once with refreshed auth for normal, compact, and streaming Codex responses
  • add regression tests for non-stream, stream, and token-invalidated detection

Closes #4087

Test plan

  • go test ./internal/runtime/executor -run 'TestCodexExecutorExecuteRefreshesAndRetriesTokenInvalidated|TestCodexExecutorExecuteStreamRefreshesAndRetriesTokenInvalidated|TestIsCodexTokenInvalidatedResponse' -count=1
  • go test ./internal/runtime/executor -count=1
  • go test ./... -count=1
  • Manual smoke with two Codex auth files on v7.2.49 latest:
    • unpatched latest: primary token_invalidated immediately suspended/fell back
    • patched latest: primary token_invalidated called refresh first, then fell back only after refresh returned refresh_token_invalidated

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

This pull request targeted main.

The base branch has been automatically changed to dev.

@github-actions github-actions Bot changed the base branch from main to dev July 2, 2026 09:21

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a token refresh and retry mechanism for the Codex executor when an authentication token is invalidated (indicated by a 401 response with a token invalidation message). This retry logic is implemented across the Execute, executeCompact, and ExecuteStream methods, and is accompanied by comprehensive unit tests. The review feedback highlights a critical issue where a failed retry request could overwrite the response variable with nil, leading to a nil pointer dereference panic in deferred body close operations. It is recommended to use temporary variables to safely handle the retry response before reassignment.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +879 to +883
httpResp, err = httpClient.Do(retryReq)
if err != nil {
helps.RecordAPIResponseError(ctx, e.cfg, err)
return resp, err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If httpClient.Do(retryReq) returns an error, httpResp will be assigned nil. Since the deferred function registered at line 830 calls httpResp.Body.Close() without a nil check, this will result in a nil pointer dereference panic.

To prevent this, use a temporary variable to capture the result of the retry request, and only reassign httpResp if the request succeeds.

Suggested change
httpResp, err = httpClient.Do(retryReq)
if err != nil {
helps.RecordAPIResponseError(ctx, e.cfg, err)
return resp, err
}
retryResp, retryErr := httpClient.Do(retryReq)
if retryErr != nil {
helps.RecordAPIResponseError(ctx, e.cfg, retryErr)
return resp, retryErr
}
httpResp = retryResp

Comment on lines +1092 to +1096
httpResp, err = httpClient.Do(retryReq)
if err != nil {
helps.RecordAPIResponseError(ctx, e.cfg, err)
return resp, err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If httpClient.Do(retryReq) returns an error, httpResp will be assigned nil. Since the deferred function registered at line 1056 calls httpResp.Body.Close() without a nil check, this will result in a nil pointer dereference panic.

To prevent this, use a temporary variable to capture the result of the retry request, and only reassign httpResp if the request succeeds.

Suggested change
httpResp, err = httpClient.Do(retryReq)
if err != nil {
helps.RecordAPIResponseError(ctx, e.cfg, err)
return resp, err
}
retryResp, retryErr := httpClient.Do(retryReq)
if retryErr != nil {
helps.RecordAPIResponseError(ctx, e.cfg, retryErr)
return resp, retryErr
}
httpResp = retryResp

Comment on lines +1252 to +1256
httpResp, err = httpClient.Do(retryReq)
if err != nil {
helps.RecordAPIResponseError(ctx, e.cfg, err)
return nil, err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency and defensive programming, use a temporary variable to capture the result of the retry request here as well. This avoids reassigning the outer httpResp and err variables before we know if the call succeeded.

Suggested change
httpResp, err = httpClient.Do(retryReq)
if err != nil {
helps.RecordAPIResponseError(ctx, e.cfg, err)
return nil, err
}
retryResp, retryErr := httpClient.Do(retryReq)
if retryErr != nil {
helps.RecordAPIResponseError(ctx, e.cfg, retryErr)
return nil, retryErr
}
httpResp = retryResp

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7457833980

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if errClose := httpResp.Body.Close(); errClose != nil {
log.Errorf("codex executor: close response body error: %v", errClose)
}
httpResp, err = httpClient.Do(retryReq)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid clobbering httpResp on failed retry

When the retry request fails at the transport layer after the initial token_invalidated 401 (for example a proxy error or context cancellation), this assignment replaces the successful first httpResp with the nil response returned by Do. The deferred close above then executes httpResp.Body.Close() and panics instead of returning the retry error; use a separate retry response variable or close the first response before reassigning. The compact retry path has the same pattern.

Useful? React with 👍 / 👎.

if httpResp.StatusCode >= 200 && httpResp.StatusCode < 300 {
goto codexReadExecuteResponse
}
b, _ = io.ReadAll(httpResp.Body)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply replay invalidation to retry errors

When the refreshed retry returns a non-2xx invalid_encrypted_content or invalid thinking-signature error, this branch reads and returns the retry body without running clearCodexReasoningReplayOnInvalidSignature, unlike the initial error path above. That leaves the bad cached reasoning replay in place after a token-invalidated first attempt, so subsequent requests can keep replaying the same invalid block instead of self-healing; run the same replay-clear check on retry error bodies before returning.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codex token_invalidated should refresh current auth before fallback

1 participant