This is an automated email from the ASF dual-hosted git repository.

hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new d8bbb991656 [fix](build) Quarantine invalidated Codex review refresh 
tokens (#69737)
d8bbb991656 is described below

commit d8bbb991656a1b87a4792f120ee80470b0b36ca0
Author: shuke <[email protected]>
AuthorDate: Wed Oct 28 16:55:28 2026 +0800

    [fix](build) Quarantine invalidated Codex review refresh tokens (#69737)
    
    Related PR: #67635
    
    Problem Summary:
    
    Code Review Runner only detects and records `refresh_token_reused`.
    Revoked sessions now fail with `refresh_token_invalidated` in Codex's
    Rust `TokenErrorDetail` stderr, while the terminal error is only
    `workspace routing discovery unauthorized (401)`. The invalid-auth
    recording step is skipped, so subsequent reviews keep selecting the
    failed credential. For example, [run
    40643700741](https://github.com/apache/doris/actions/runs/40643700741)
    reproduces this sequence.
    
    Recognize both permanent refresh failures in terminal events and Codex
    stderr, including `error_code: Some(...)`. Pass the detected reason
    through the quarantine condition, marker JSON, and failure notification.
    Preserve the existing per-refresh-token hash marker so replacing a token
    restores eligibility and late failures cannot quarantine its
    replacement.
    
    Generic 401/`token_revoked`, tool-output quotations, and successful
    reviews remain excluded from permanent quarantine. Only the workflow and
    its offline tests change. The tests also supply the required convergence
    fields in the fake final submission so existing success-path assertions
    can execute.
---
 .github/scripts/test_review_auth_quarantine.py | 131 +++++++++++++++++++++++--
 .github/workflows/code-review-runner.yml       |  53 ++++++----
 2 files changed, 158 insertions(+), 26 deletions(-)

diff --git a/.github/scripts/test_review_auth_quarantine.py 
b/.github/scripts/test_review_auth_quarantine.py
index d458b6dbf3b..6557a6af9df 100755
--- a/.github/scripts/test_review_auth_quarantine.py
+++ b/.github/scripts/test_review_auth_quarantine.py
@@ -39,6 +39,13 @@ from pathlib import Path
 
 WORKFLOW = Path(__file__).resolve().parents[1] / 
"workflows/code-review-runner.yml"
 PREFIX = "oss://doris-community-ci/codex/"
+INVALIDATED_MESSAGE = "Your session has ended. Please log in again."
+INVALIDATED_STDERR = (
+    '2026-10-28T06:26:31.373554Z ERROR codex_login::auth::manager: '
+    'Failed to refresh token status=401 Unauthorized detail=TokenErrorDetail { 
'
+    'error_code: Some("refresh_token_invalidated"), '
+    'error_message: Some("Your session has ended. Please log in again."), .. }'
+)
 REUSED_MESSAGE = (
     "Your access token could not be refreshed because your refresh token was 
already used. "
     "Please log out and sign in again."
@@ -106,7 +113,9 @@ import sys
 if os.environ.get("FAKE_FINAL_REVIEW") == "1":
     import json, subprocess
     request = Path(os.environ["REVIEW_CONTEXT_DIR"], "final-input.json")
-    request.write_text(json.dumps({"body": "Complete review", "comments": [], 
"existing_blocking_comment_ids": []}))
+    request.write_text(json.dumps({"body": "Complete review", "comments": [],
+                                   "existing_blocking_comment_ids": [], 
"converged": True,
+                                   "rounds": 1, "incomplete_reason": ""}))
     subprocess.run([sys.executable, str(Path(os.environ["RUNNER_TEMP"], 
"submit_review.py")),
                     "--context-dir", os.environ["REVIEW_CONTEXT_DIR"], 
"--input-file", str(request)],
                    check=True, stdout=subprocess.DEVNULL)
@@ -209,10 +218,13 @@ class ReviewAuthQuarantineTest(unittest.TestCase):
             context = Path(tempfile.mkdtemp(dir=self.env["RUNNER_TEMP"]))
             (context / "codex_goal_prompt.txt").write_text("Fake review")
             self.env["REVIEW_CONTEXT_DIR"] = str(context)
+        return self.run_script(step_script(name), expected=expected, **env)
+
+    def run_script(self, script, expected=0, **env):
         output = Path(self.env["GITHUB_OUTPUT"])
         output.write_text("")
         result = subprocess.run(
-            ["bash", "--noprofile", "--norc", "-eo", "pipefail", "-c", 
step_script(name)],
+            ["bash", "--noprofile", "--norc", "-eo", "pipefail", "-c", script],
             env={**self.env, **env}, cwd=self.root, capture_output=True, 
text=True, timeout=15, check=False,
         )
         self.assertEqual(result.returncode, expected, result.stdout + 
result.stderr)
@@ -239,7 +251,8 @@ class ReviewAuthQuarantineTest(unittest.TestCase):
     def configure(self, expected=0, **env):
         return self.run_step("Configure Codex auth", expected=expected, **env)
 
-    def fail_review(self, events=None, stderr="", expected_invalid=True, 
**env):
+    def fail_review(self, events=None, stderr="", expected_invalid=True,
+                    expected_reason="refresh_token_reused", **env):
         if events is None:
             events = [{"type": "turn.failed", "error": {"message": 
REUSED_MESSAGE}}]
         logs, outputs = self.run_step(
@@ -247,11 +260,106 @@ class ReviewAuthQuarantineTest(unittest.TestCase):
             FAKE_CODEX_EVENTS="\n".join(json.dumps(event) for event in events),
             FAKE_CODEX_STDERR=stderr, **env,
         )
-        self.assertEqual("auth_invalid_reason=refresh_token_reused\n" in 
outputs, expected_invalid, outputs)
+        self.assertEqual(f"auth_invalid_reason={expected_reason}\n" in 
outputs, expected_invalid, outputs)
         return logs, outputs
 
-    def record(self, expected=0, **env):
-        return self.run_step("Record invalid Codex auth", expected=expected, 
**env)
+    def record(self, expected=0, reason="refresh_token_reused", **env):
+        return self.run_step("Record invalid Codex auth", expected=expected,
+                             AUTH_INVALID_REASON=reason, **env)
+
+    def classify_failure(self, events=(), stderr="", status=1):
+        # Execute the real workflow's post-run shell independently of Linux
+        # process supervision, so classification and OSS tests also run on 
macOS.
+        context = Path(self.env["REVIEW_CONTEXT_DIR"])
+        (context / "codex-events.jsonl").write_text(
+            "\n".join(json.dumps(event) for event in events))
+        (context / "codex-stderr.log").write_text(stderr)
+        (context / "review-result.json").write_text(json.dumps({
+            "state": "success", "p0": 0, "p1": 0, "converged": True, "rounds": 
1,
+        }))
+        script = step_script("Run automated code review")
+        script = script[script.index('failure_reason=""'):]
+        _, outputs = self.run_script(script, expected=int(status != 0), 
status=str(status))
+        return next((line.split("=", 1)[1] for line in outputs.splitlines()
+                     if line.startswith("auth_invalid_reason=")), "")
+
+    def 
test_invalidated_stderr_is_quarantined_and_replacement_is_eligible(self):
+        self.put_auth()
+        self.put_auth(3, "refresh-other")
+        self.configure()
+        # The terminal message hides the permanent refresh failure on stderr.
+        reason = self.classify_failure(events=[{
+            "type": "turn.failed", "error": {"message": "workspace routing 
discovery unauthorized (401)"},
+        }], stderr=INVALIDATED_STDERR)
+        self.assertEqual(reason, "refresh_token_invalidated")
+        self.record(reason=reason)
+        marker = json.loads(self.marker().read_text())
+        self.assertEqual(marker["reason"], reason)
+        self.assertNotIn("refresh-old", self.marker().read_text())
+        self.new_runner()
+        logs, _ = self.configure()
+        self.assertIn("Skipping Codex auth auth.json.2", logs)
+        self.assertEqual(self.env["CODEX_AUTH_OSS_OBJECT"], PREFIX + 
"auth.json.3")
+        self.put_auth(refresh="refresh-new")
+        self.new_runner()
+        self.configure()
+        self.assertEqual(self.env["CODEX_AUTH_OSS_OBJECT"], PREFIX + 
"auth.json.2")
+        self.assertFalse(self.marker(refresh="refresh-new").exists())
+
+    def test_permanent_auth_codes_in_terminal_events_and_stderr(self):
+        for code, message in (("refresh_token_reused", REUSED_MESSAGE),
+                              ("refresh_token_invalidated", 
INVALIDATED_MESSAGE)):
+            samples = [
+                ([{"type": "turn.failed", "error": {"code": code, "message": 
"Refresh failed"}}], ""),
+                ([{"type": "error", "code": code, "message": "Refresh 
failed"}], ""),
+                ([{"type": "error", "message": code}], ""),
+                ([{"type": "error", "message": message}], ""),
+                ([], json.dumps({"error": {"code": code}})),
+                ([], INVALIDATED_STDERR.replace("refresh_token_invalidated", 
code)),
+            ]
+            for events, stderr in samples:
+                with self.subTest(code=code, events=events, stderr=stderr):
+                    self.assertEqual(self.classify_failure(events, stderr), 
code)
+
+    def test_auth_like_tool_output_and_generic_401_do_not_quarantine(self):
+        for message in ("401 Unauthorized", "workspace routing discovery 
unauthorized (401)",
+                        "token_revoked", "Request timed out", "You've hit your 
usage limit"):
+            with self.subTest(message=message):
+                self.assertEqual(self.classify_failure(events=[
+                    {"type": "item.completed", "item": {"type": 
"command_execution",
+                     "aggregated_output": INVALIDATED_STDERR + 
INVALIDATED_MESSAGE}},
+                    {"type": "turn.failed", "error": {"message": message}},
+                ], stderr="ERROR codex_models_manager::manager: auth error 
code: token_revoked"), "")
+
+    def test_success_does_not_quarantine_prior_invalidated_stderr(self):
+        self.assertEqual(self.classify_failure(
+            events=[{"type": "error", "code": "refresh_token_invalidated"},
+                    {"type": "turn.completed"}], stderr=INVALIDATED_STDERR, 
status=0), "")
+
+    def test_invalidated_marker_write_failure_and_notification(self):
+        self.put_auth()
+        self.configure()
+        self.record(expected=1, reason="refresh_token_invalidated",
+                    FAKE_OSS_FAIL="cp:" + PREFIX + self.marker().name)
+        self.assertFalse(self.marker().exists())
+        for outcome in ("success", "failure"):
+            with self.subTest(outcome=outcome):
+                self.run_step(
+                    "Comment PR on review failure", AUTH_FAILURE_REASON="",
+                    AUTH_INVALID_REASON="refresh_token_invalidated", 
AUTH_INVALID_RECORD_OUTCOME=outcome,
+                    REVIEW_FAILURE_REASON=INVALIDATED_MESSAGE, 
REVIEW_CONTEXT_OUTCOME="success",
+                    REVIEW_OUTCOME="failure", USAGE_LIMIT_RETRY_AFTER_EPOCH="",
+                    RUN_URL="https://example.test/run";,
+                )
+                body = Path(self.env["FAKE_COMMENT_FILE"]).read_text()
+                self.assertEqual("excluded from future reviews" in body, 
outcome == "success")
+                self.assertEqual("may still be selected" in body, outcome == 
"failure")
+
+    def test_unknown_auth_reason_cannot_write_marker(self):
+        self.put_auth()
+        self.configure()
+        self.record(expected=1, reason="401 Unauthorized")
+        self.assertFalse(self.marker().exists())
 
     def test_reused_token_is_persisted_and_skipped_on_next_run(self):
         self.put_auth()
@@ -343,7 +451,7 @@ class ReviewAuthQuarantineTest(unittest.TestCase):
         }))
         _, outputs = self.configure(expected=1)
         self.assertIn("Earliest usage-limit retry", outputs)
-        self.assertIn("reused refresh tokens must be replaced", outputs)
+        self.assertIn("invalid refresh tokens must be replaced", outputs)
 
     def test_marker_lookup_failure_does_not_select_token(self):
         self.put_auth()
@@ -388,6 +496,11 @@ class ReviewAuthQuarantineTest(unittest.TestCase):
     def test_codex_stderr_reused_code(self):
         self.fail_review(events=[], stderr='Token refresh failed: 401 
Unauthorized\n{\n  "error": {\n    "code": "refresh_token_reused"\n  }\n}')
 
+    def 
test_codex_stderr_invalidated_code_survives_runner_failure_summary(self):
+        self.fail_review(events=[{
+            "type": "error", "message": "workspace routing discovery 
unauthorized (401)",
+        }], stderr=INVALIDATED_STDERR, 
expected_reason="refresh_token_invalidated")
+
     def test_unrelated_failures_are_not_quarantined(self):
         for message in ("401 Unauthorized", "We're experiencing high demand", 
"Request timed out", "You've hit your usage limit"):
             with self.subTest(message=message):
@@ -465,7 +578,9 @@ class ReviewAuthQuarantineTest(unittest.TestCase):
     def 
test_marker_step_runs_after_failed_review_with_explicit_detection(self):
         step = workflow_step("Record invalid Codex auth")
         self.assertIn("always() && steps.auth.outcome == 'success'", step)
-        self.assertIn("steps.review.outputs.auth_invalid_reason == 
'refresh_token_reused'", step)
+        self.assertIn("contains(fromJSON(", step)
+        self.assertIn('"refresh_token_reused","refresh_token_invalidated"', 
step)
+        self.assertIn("steps.review.outputs.auth_invalid_reason)", step)
         self.assertNotIn("continue-on-error: true", step)
 
 
diff --git a/.github/workflows/code-review-runner.yml 
b/.github/workflows/code-review-runner.yml
index 0db0b373032..24e21cc2524 100644
--- a/.github/workflows/code-review-runner.yml
+++ b/.github/workflows/code-review-runner.yml
@@ -471,7 +471,7 @@ jobs:
               exit 1
             fi
             if grep -Fxq "$invalid_object" "$invalid_listing"; then
-              echo "Skipping Codex auth ${candidate##*/}: refresh token was 
already used."
+              echo "Skipping Codex auth ${candidate##*/}: refresh token is 
marked invalid."
               rm -f "$candidate_auth"
               continue
             fi
@@ -484,12 +484,12 @@ jobs:
           done < <(shuf "$auth_candidates")
 
           if [ -z "$auth_object" ]; then
-            auth_failure_reason="No eligible Codex review credentials: all are 
usage-limited or have a reused refresh token."
+            auth_failure_reason="No eligible Codex review credentials: all are 
usage-limited or have an invalid refresh token."
             if [ -n "$earliest_retry_after_epoch" ]; then
               retry_at="$(date -u -d "@$earliest_retry_after_epoch" 
+%Y-%m-%dT%H:%M:%SZ)"
               auth_failure_reason="$auth_failure_reason Earliest usage-limit 
retry is $retry_at."
             fi
-            auth_failure_reason="$auth_failure_reason Credentials with reused 
refresh tokens must be replaced."
+            auth_failure_reason="$auth_failure_reason Credentials with invalid 
refresh tokens must be replaced."
             echo "failure_reason=$auth_failure_reason" >> "$GITHUB_OUTPUT"
             echo "::error::$auth_failure_reason"
             exit 1
@@ -951,16 +951,28 @@ jobs:
               failure_reason="Codex exited with status $status"
             fi
 
-            # Only terminal auth errors and Codex's own stderr identify an 
invalid
-            # token. A tool result quoting this error must not quarantine 
credentials.
-            if grep -Fqi "Your access token could not be refreshed because 
your refresh token was already used." <<<"$failure_reason" || \
-               grep -Fxq "refresh_token_reused" <<<"$failure_reason" || \
-               { jq -r 'select(.type == "turn.failed" or .type == "error") | 
.error.code // .code // empty' \
-                   "$REVIEW_CONTEXT_DIR/codex-events.jsonl" | grep -Fx 
"refresh_token_reused" >/dev/null; } || \
-               grep -Eq 
'"code"[[:space:]]*:[[:space:]]*"refresh_token_reused"' 
"$REVIEW_CONTEXT_DIR/codex-stderr.log"; then
-              echo "auth_invalid_reason=refresh_token_reused" >> 
"$GITHUB_OUTPUT"
-              echo "::warning::Codex refresh token was already used; recording 
this credential version as invalid."
-            fi
+            # Only permanent refresh failures quarantine the selected token.
+            # Access-token 401/token_revoked alone can still recover by 
refresh;
+            # tool results quoting auth errors are not authentication evidence.
+            for auth_invalid_reason in refresh_token_reused 
refresh_token_invalidated; do
+              case "$auth_invalid_reason" in
+                refresh_token_reused)
+                  auth_invalid_message="Your access token could not be 
refreshed because your refresh token was already used." ;;
+                refresh_token_invalidated)
+                  auth_invalid_message="Your session has ended. Please log in 
again." ;;
+              esac
+              if grep -Fqi "$auth_invalid_message" <<<"$failure_reason" || \
+                 grep -Fxq "$auth_invalid_reason" <<<"$failure_reason" || \
+                 { jq -r 'select(.type == "turn.failed" or .type == "error") | 
.error.code // .code // empty' \
+                     "$REVIEW_CONTEXT_DIR/codex-events.jsonl" | grep -Fx 
"$auth_invalid_reason" >/dev/null; } || \
+                 grep -Eq 
"\"code\"[[:space:]]*:[[:space:]]*\"${auth_invalid_reason}\"" 
"$REVIEW_CONTEXT_DIR/codex-stderr.log" || \
+                 grep -Eq "ERROR codex_login::auth(::manager)?: Failed to 
refresh token .*error_code: Some\(\"${auth_invalid_reason}\"\)" \
+                   "$REVIEW_CONTEXT_DIR/codex-stderr.log"; then
+                echo "auth_invalid_reason=$auth_invalid_reason" >> 
"$GITHUB_OUTPUT"
+                echo "::warning::Codex refresh failed ($auth_invalid_reason); 
recording this credential version as invalid."
+                break
+              fi
+            done
 
             usage_limit_retry_after_epoch=""
             if grep -Fqi "You've hit your usage limit" <<<"$failure_reason"; 
then
@@ -1017,20 +1029,25 @@ jobs:
 
       - name: Record invalid Codex auth
         id: auth_invalid_record
-        if: ${{ always() && steps.auth.outcome == 'success' && 
steps.review.outputs.auth_invalid_reason == 'refresh_token_reused' }}
+        if: ${{ always() && steps.auth.outcome == 'success' && 
contains(fromJSON('["refresh_token_reused","refresh_token_invalidated"]'), 
steps.review.outputs.auth_invalid_reason) }}
         timeout-minutes: 5
         env:
+          AUTH_INVALID_REASON: ${{ steps.review.outputs.auth_invalid_reason }}
           OSS_AK: ${{ secrets.OSS_AK }}
           OSS_SK: ${{ secrets.OSS_SK }}
           OSS_ENDPOINT: oss-cn-hongkong.aliyuncs.com
         run: |
+          case "$AUTH_INVALID_REASON" in
+            refresh_token_reused|refresh_token_invalidated) ;;
+            *) echo "::error::Unsupported Codex auth invalidation reason."; 
exit 1 ;;
+          esac
           # Bind the marker to the selected snapshot, so a late failing job 
cannot
           # invalidate a replacement uploaded by another job or an operator.
           
refresh_token_hash="$(<"$RUNNER_TEMP/codex-auth-refresh-token.sha256")"
           [[ "$refresh_token_hash" =~ ^[0-9a-f]{64}$ ]]
           invalid_file="$(mktemp "$RUNNER_TEMP/codex-auth-invalid.XXXXXX")"
-          jq -n --arg refresh_token_sha256 "$refresh_token_hash" \
-            '{version: 1, state: "auth_invalid", reason: 
"refresh_token_reused", refresh_token_sha256: $refresh_token_sha256}' \
+          jq -n --arg refresh_token_sha256 "$refresh_token_hash" --arg reason 
"$AUTH_INVALID_REASON" \
+            '{version: 1, state: "auth_invalid", reason: $reason, 
refresh_token_sha256: $refresh_token_sha256}' \
             > "$invalid_file"
 
           # A separate object per token is idempotent and cannot be 
overwritten by
@@ -1083,8 +1100,8 @@ jobs:
           recovery_msg="Please inspect the workflow logs and rerun the review 
after the underlying issue is resolved."
           if [ -n "$AUTH_FAILURE_REASON" ]; then
             error_msg="$AUTH_FAILURE_REASON"
-          elif [ "$AUTH_INVALID_REASON" = "refresh_token_reused" ]; then
-            error_msg="${REVIEW_FAILURE_REASON:-The selected Codex refresh 
token was already used.}"
+          elif [ "$AUTH_INVALID_REASON" = "refresh_token_reused" ] || [ 
"$AUTH_INVALID_REASON" = "refresh_token_invalidated" ]; then
+            error_msg="${REVIEW_FAILURE_REASON:-The selected Codex refresh 
token is invalid.}"
             if [ "$AUTH_INVALID_RECORD_OUTCOME" = "success" ]; then
               recovery_msg="This credential version is excluded from future 
reviews. Replace its refresh token to restore eligibility. Please trigger 
/review again; another configured account may be available."
             else


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to