anthropics / anthropics/claude-code-action

claude_code_oauth_token from claude setup-token rejected with 401 in the action, while the same account authenticates fine locally

Aperta
#1,614 8 commenti 3 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
8.9k
Fork
2.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

# `claude_code_oauth_token` from `claude setup-token` rejected with 401 in the action, while the same account authenticates fine locally

## Summary

Every run of `anthropics/claude-code-action` fails ~2 seconds after Claude Code initializes, with:

```
"error_status": 401,
"error": "authentication_failed",
"api_error_status": 401,
"terminal_reason": "api_error",
"result": "Failed to authenticate. API Error: 401 OAuth access token is invalid."
```

The action reports this as `subtype: "success"` with `is_error: true`, which surfaces to the job as `Claude execution failed: result is_error:true` — the underlying 401 is only visible with `show_full_output: true`.

The same account authenticates and completes requests fine via the local CLI. Four separately minted tokens produce the identical 401, on two different pinned action versions and two different models.

## Environment

| | |
|---|---|
| Action version | `v1.0.189` (`6b082c41935b4c8a3b8b0ef85ba4ba4d9eeb8975`) — also reproduced on `v1.0.183` |
| Runner | `ubuntu-latest` (GitHub-hosted) |
| Local CLI | Claude Code `2.1.226` |
| Auth method | `claude setup-token` → repo secret → `claude_code_oauth_token` input |
| Account | Max subscription, `authMethod: claude.ai`, `apiProvider: firstParty` |
| Model | `--model claude-opus-5` — also reproduced on `claude-opus-4-8` |
| Repo | Private |

## Reproduction

1. `claude auth login` (browser flow completes)
2. Verify the session is live:
```
$ claude auth status
{ "loggedIn": true, "authMethod": "claude.ai", "apiProvider": "firstParty",
"subscriptionType": "max", ... }

$ claude --model claude-opus-5 -p "say OK"
OK
```
3. `claude setup-token`, paste the result into the repo secret:
```
gh secret set CLAUDE_CODE_OAUTH_TOKEN -R /
```
4. Trigger the workflow (`@claude` comment on a PR, or a `pull_request` event).
5. The run fails ~2s after init with the 401 above. `num_turns: 1`, `total_cost_usd: 0`.

Step 3→4 was performed within roughly one minute in the last attempt, so token age is not a factor.

## What we ruled out

| Hypothesis | Result |
|---|---|
| Token expired | No — reproduced with a token minted <1 min before the run |
| Stale/mistyped secret value | No — **four** separately minted tokens, identical 401; set via `gh secret set` (stdin, no paste-wrapping) |
| Local session expired | Was true initially, then fixed. Post-`auth login` the CLI works; the action still 401s |
| Account lacks model access | No — `claude --model claude-opus-5 -p "say OK"` returns `OK` on the same account |
| Model-specific | No — identical on `claude-opus-4-8` and `claude-opus-5` |
| Action version | No — identical on `v1.0.183` and `v1.0.189` |
| Workflow misconfiguration | No — checkout succeeds, the action's own permission check passes (`Permission level retrieved: admin`, `Trigger result: true`), Claude Code initializes with the right model, then the first authenticated call 401s |
| Rate limiting / usage cap | Not indicated — the error is `authentication_failed` / 401, not 429 |

Additional detail: `total_cost_usd: 0` on every failing run. On a Max-plan OAuth token that is the expected value regardless of outcome (usage bills against the subscription), so it is *not* offered here as evidence of anything — noting it only because it is easy to misread.

## Expected vs actual

**Expected:** a token minted by `claude setup-token` from a live, logged-in Max session authenticates in the action the same way it does locally.

**Actual:** the action's first authenticated request is rejected with `401 OAuth access token is invalid`.

## Secondary issue: the failure is undiagnosable by default

With default settings, the only signal is:

```
##[error]Claude result reported subtype success with is_error:true (run did not complete successfully)
##[error]Action failed with error: Claude execution failed: result is_error:true
```

The actual cause (`401 ... token is invalid`) requires `show_full_output: true`, which also prints the full model output — including the PR diff and review prompt — into the step log. That is a poor tradeoff for reading one auth error. Surfacing `error`/`error_status`/`terminal_reason` in the failure annotation, while keeping model output redacted, would make this class of failure self-diagnosing.

Related: the action reports `subtype: "success"` alongside `is_error: true`, which is contradictory and makes log-grepping for failures harder than it needs to be.

## Also worth noting (may be unrelated)

Two runs in a sibling repo reported job conclusion `success` while actually having skipped:

```
##[warning]Skipping action due to workflow validation: Workflow validation failed.
The workflow file must exist and have identical content to the version on the
repository's default branch.
```

The self-skip on workflow-file PRs is documented and expected, but reporting it as `success` makes "did the review actually run?" impossible to answer from the conclusion alone. A `skipped` conclusion (or a distinct marker) would be clearer.

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.