Skip to content

fix: detect Claude Code 401 auth errors in claude_harness#45982

Merged
pelikhan merged 7 commits into
mainfrom
copilot/fix-issue-29495726767
Jul 16, 2026
Merged

fix: detect Claude Code 401 auth errors in claude_harness#45982
pelikhan merged 7 commits into
mainfrom
copilot/fix-issue-29495726767

Conversation

Copilot AI commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

AUTHENTICATION_FAILED_PATTERN only matched Anthropic-direct auth text ("Authentication failed (Request ID: ...)"), missing the signals Claude Code emits when auth fails via the AWF API proxy — causing the harness to burn all 4 retry attempts instead of aborting immediately.

Changes

  • claude_harness.cjs — extend AUTHENTICATION_FAILED_PATTERN to cover the two additional auth failure signals:

    // Before
    /Authentication failed(?:\s*\(Request ID:[^)]+\))?/i
    
    // After
    /(?:Authentication failed(?:\s*\(Request ID:[^)]+\))?|"error"\s*:\s*"authentication_failed"|not logged in)/i
    • "error":"authentication_failed" — JSON field in Claude Code stream output on 401
    • not logged in — text content ("Not logged in · Please run /login") emitted when no credentials are present
  • claude_harness.test.cjs — add three test cases for the new pattern branches.

Copilot AI and others added 3 commits July 16, 2026 12:09
…th errors

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…tication_failed)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…group

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review July 16, 2026 12:15
Copilot AI review requested due to automatic review settings July 16, 2026 12:15
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (default_business_additions=0).

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Copilot AI 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.

Pull request overview

Extends Claude harness authentication detection to stop retries on Claude Code 401 failures.

Changes:

  • Detects proxy and missing-login authentication signals.
  • Adds regression tests for the new patterns.
Show a summary per file
File Description
actions/setup/js/claude_harness.cjs Expands authentication-error matching.
actions/setup/js/claude_harness.test.cjs Tests new authentication signals.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Medium

const jsonLine = JSON.stringify({
type: "assistant",
error: "authentication_failed",
message: { content: [{ type: "text", text: "Not logged in · Please run /login" }] },

@github-actions github-actions 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.

The regex extension and tests look correct. No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 7.72 AIC · ⌖ 6.66 AIC · ⊞ 5K

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot generalize this detection to common harness code

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 3 test(s): 3 design, 0 implementation, 0 violation(s).

📊 Metrics (3 tests)
Metric Value
Analyzed 3 (Go: 0, JS: 3)
✅ Design 3 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 3 (100%)
Duplicate clusters 0
Inflation YES (test +17 / prod +5 = 3.4:1)
🚨 Violations 0
Test File Classification Issues
returns true for stream-JSON error:authentication_failed claude_harness.test.cjs:192 design_test None
returns true for Not logged in message claude_harness.test.cjs:201 design_test None
returns true for not logged in (case-insensitive) claude_harness.test.cjs:205 design_test None
⚠️ Flagged Tests (1 — test inflation only)

claude_harness.test.cjs — Test inflation: +17 lines added vs +5 production lines (3.4:1, threshold 2:1). Not a blocking violation — the tests are concise and high-value; the ratio is driven by the low production delta, not test bloat.

Verdict

Passed. 0% implementation tests (threshold: 30%). All 3 new tests verify behavioral contracts of isAuthenticationFailedError against newly supported error patterns (authentication_failed JSON field, "not logged in" literal, case-insensitive matching).

References: §29497427965

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 33.6 AIC · ⌖ 8.63 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions 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.

✅ Test Quality Sentinel: 90/100. 0% implementation tests (threshold: 30%).

@github-actions github-actions 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.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 26.9 AIC · ⌖ 4.46 AIC · ⊞ 6.7K
Comment /matt to run again

Comment thread actions/setup/js/claude_harness.cjs Outdated
const RATE_LIMIT_ERROR_PATTERN = /rate_limit_error|429 Too Many Requests|"api_error_status"\s*:\s*429|request rejected \(429\)|rate limit/i;
const AUTHENTICATION_FAILED_PATTERN = /Authentication failed(?:\s*\(Request ID:[^)]+\))?/i;
// Matches:
// - "Authentication failed (Request ID: ...)" — Anthropic-direct auth error

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.

[/diagnosing-bugs] The not logged in branch is quite generic — it could produce false positives if any tool, test, or user script that Claude Code runs emits this phrase as part of its own output (e.g. a git command, a test assertion, or a CLI tool).

💡 Suggestion: tighten the match or add a negative test

Consider anchoring it more tightly, e.g.:

/Not logged in(?:\s*[·]\s*Please run)?/i

This still matches the known Claude Code message ("Not logged in · Please run /login") while avoiding accidental matches on arbitrary application output that happens to contain the phrase.

If you intentionally want the broad match, add an explicit negative test that confirms a message like "User is not logged in to the database" does not trigger auth abort — so any future accidental broadening is caught.

@copilot please address this.

});

it('returns true for Claude Code "Not logged in" message', () => {
expect(isAuthenticationFailedError("Not logged in · Please run /login")).toBe(true);

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.

[/tdd] The test on line 203 exercises a JSON-serialised line containing "error":"authentication_failed" — great. However, it does not test the negative/boundary case: a JSON line where "error" has a different value (e.g. "rate_limit_error") should not match this pattern.

💡 Suggested negative test
it("does not confuse a different JSON error field with auth failure", () => {
  const jsonLine = JSON.stringify({ type: "error", error: "rate_limit_error" });
  expect(isAuthenticationFailedError(jsonLine)).toBe(false);
});

This guards against regex drift that might accidentally widen the authentication_failed branch to other JSON error values.

@copilot please address this.

@github-actions github-actions 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.

Two issues block merge

The fix correctly targets a real problem (missed auth failure signals burning retry budget), but introduces a false-positive risk and ships a test that doesn't actually cover what it claims.

Blocking findings

1. pattern is unanchored (high)
The regex matches any substring anywhere in the full accumulated stdout+stderr. Task content being processed (web pages, files, commit messages) could contain this phrase and trigger a non-retryable abort on attempt 0. The "error":"authentication_failed" arm is much safer because it is a JSON key-value match. The not logged in arm needs anchoring (start-of-line, word boundaries, or requiring the Claude Code /login suffix).

2. JSON test provides no coverage of the "error" arm (high)
The test embeds "Not logged in" inside the JSON text field, so the not logged in branch alone makes it pass. Deleting the "error":"authentication_failed" arm entirely leaves all tests green. The test must use a neutral text value to isolate the branch it claims to test.

🔎 Code quality review by PR Code Quality Reviewer · 37.4 AIC · ⌖ 4.67 AIC · ⊞ 5.6K
Comment /review to run again

Comment thread actions/setup/js/claude_harness.cjs Outdated
// - "Authentication failed (Request ID: ...)" — Anthropic-direct auth error
// - `"error":"authentication_failed"` — Claude Code stream-JSON error field (e.g. 401 via AWF proxy)
// - "Not logged in" — Claude Code message when no credentials are available
const AUTHENTICATION_FAILED_PATTERN = /(?:Authentication failed(?:\s*\(Request ID:[^)]+\))?|"error"\s*:\s*"authentication_failed"|not logged in)/i;

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.

False-positive risk: not logged in matches any substring in accumulated stdout/stderr and will abort retries on attempt 0 if task output happens to contain that phrase.

💡 Details and suggested fix

The pattern /not logged in/i has no anchoring, word boundaries, or structural requirements. The harness collects all stdout+stderr from the subprocess — including tool responses, task descriptions, and user-provided content being processed. If any line in that output contains "not logged in" (e.g., a web-scraping result, a git commit message, a markdown file being read), isAuthenticationFailedError returns true and the harness hits the early-abort path at attempt 0 (non-retryable), silently killing the job.

Since this signal originates from Claude Code itself, consider anchoring it more tightly — e.g., requiring it to appear as a standalone line or within a JSON event field:

// Option A: require it to appear at the start of a line (common for Claude Code terminal messages)
/(?:Authentication failed(?:\s*\(Request ID:[^)]+\))?|"error"\s*:\s*"authentication_failed"|^not logged in\b)/im

// Option B: require word boundaries and limit to short strings
/(?:Authentication failed(?:\s*\(Request ID:[^)]+\))?|"error"\s*:\s*"authentication_failed"|(?<![a-z])not logged in(?![a-z])\s*[·—·]?\s*(?:please run \/login)?)/i

At minimum add a test that confirms output containing "not logged in" buried inside unrelated content (e.g., "Error: user not logged in to external service") does not trigger the pattern unintentionally — or acknowledge the risk in a comment.

it('returns true for Claude Code stream-JSON "error":"authentication_failed" field', () => {
const jsonLine = JSON.stringify({
type: "assistant",
error: "authentication_failed",

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.

Test does not isolate the "error" field branch: the JSON payload also embeds "Not logged in" in the text field, so this test passes even if the "error":"authentication_failed" regex arm were deleted.

💡 Suggested fix

Use a neutral string for the text field so only the error key triggers the match:

it('returns true for Claude Code stream-JSON "error":"authentication_failed" field', () => {
  const jsonLine = JSON.stringify({
    type: "assistant",
    error: "authentication_failed",
    // Neutral text — must NOT independently satisfy the pattern
    message: { content: [{ type: "text", text: "Please authenticate to continue." }] },
  });
  expect(isAuthenticationFailedError(jsonLine)).toBe(true);
});

Without this fix there is zero actual coverage for the "error"\s*:\s*"authentication_failed" branch — it can be deleted and all tests still pass.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ss_retry_guard

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

@copilot generalize this detection to common harness code

Done in c578de5. AUTHENTICATION_FAILED_PATTERNS (the three-alternative pattern covering Authentication failed..., "error":"authentication_failed", and not logged in) and a common isAuthenticationFailedError are now in harness_retry_guard.cjs and exported alongside the other guard utilities.

  • claude_harness.cjs and codex_harness.cjs now import and re-export isAuthenticationFailedError directly from harness_retry_guard.cjs.
  • copilot_harness.cjs imports the common function as isCommonAuthenticationFailedError and wraps it locally with its existing Copilot-specific PAT pattern (checking third-party user token: ... Personal Access Tokens are not supported).
  • Tests for the common function added to harness_retry_guard.test.cjs. All 364 harness tests pass.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟢 Low
Score 60 (impact 28 + urgency 18 + quality 14)
Action batch_review
Batch bug-small (with #45985)

Rationale: Targeted Claude Code auth error detection fix (3 files, +29/-3). AI bot APPROVED. Prevents wasted retry cycles on 401 auth failures. Batch with #45985 (similar small bug fixes).

Generated by 🔧 PR Triage Agent · 32.8 AIC · ⌖ 4.9 AIC · ⊞ 5.6K ·

@pelikhan
pelikhan merged commit 8f8b1cf into main Jul 16, 2026
14 checks passed
@pelikhan
pelikhan deleted the copilot/fix-issue-29495726767 branch July 16, 2026 13:20
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.11

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants