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

Abierto
#1,614 8 comentarios 3 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
TypeScript
Estrellas
8.9k
Forks
2.1k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

# `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.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.