Skip to content

fix: always apply Anthropic beta headers wrapper for OAuth token support#41461

Open
hnykda wants to merge 1 commit intoopenclaw:mainfrom
hnykda:fix/oauth-beta-unconditional
Open

fix: always apply Anthropic beta headers wrapper for OAuth token support#41461
hnykda wants to merge 1 commit intoopenclaw:mainfrom
hnykda:fix/oauth-beta-unconditional

Conversation

@hnykda
Copy link
Copy Markdown
Contributor

@hnykda hnykda commented Mar 9, 2026

Summary

  • Always apply createAnthropicBetaHeadersWrapper for Anthropic providers, not just when resolveAnthropicBetas() returns non-empty
  • This ensures the oauth-2025-04-20 beta is injected for OAuth tokens (sk-ant-oat-*) even when context1m is configured via model-level headers instead of agent extra params

Problem

When context-1m-2025-08-07 is set in models.providers.anthropic.models[].headers (model-level) rather than in agents.defaults.models[].params.context1m (extra params), resolveAnthropicBetas() returns undefined, so createAnthropicBetaHeadersWrapper is never applied. The pi-ai SDK detects the sk-ant-oat prefix and uses Authorization: Bearer, but without the required oauth-2025-04-20 beta header, Anthropic rejects with:

HTTP 401 authentication_error: OAuth authentication is currently not supported.

The fix from #19789 correctly handles this inside the wrapper, but the wrapper was gated behind a non-empty betas check that prevented it from running.

Changes

extra-params.ts: Always apply the beta headers wrapper for provider === "anthropic", passing an empty array when no explicit betas are configured. The wrapper's internal OAuth detection then runs unconditionally.

pi-embedded-runner-extraparams.test.ts: Updated existing test and added new test for OAuth tokens without context1m configured.

Test plan

  • All 67 existing tests pass
  • New test: OAuth betas injected for sk-ant-oat tokens even without context1m
  • Verified fix resolves the issue on a live deployment

Fixes #41444
Related: #19769, #19789

🤖 Generated with Claude Code

The `createAnthropicBetaHeadersWrapper` was only applied when
`resolveAnthropicBetas` returned non-empty (i.e., when `context1m` or
`anthropicBeta` was set in agent extra params). When `context-1m` was
configured via model-level headers instead of extra params, the wrapper
was never applied, so the critical `oauth-2025-04-20` beta was never
injected for OAuth tokens (`sk-ant-oat-*`). This caused Anthropic to
reject all requests with HTTP 401 "OAuth authentication is currently
not supported."

Now the wrapper is always applied for Anthropic providers, ensuring
OAuth token detection and beta injection runs unconditionally.

Fixes openclaw#41444
Related: openclaw#19769, openclaw#19789

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS labels Mar 9, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 9, 2026

Greptile Summary

This PR fixes a critical bug where the createAnthropicBetaHeadersWrapper was not applied when no explicit betas (e.g. context1m) were configured via extra params, causing OAuth tokens (sk-ant-oat-*) to fail with HTTP 401 because the required oauth-2025-04-20 beta header was never injected.

Key changes:

  • extra-params.ts: The guard condition if (anthropicBetas?.length) is replaced with if (provider === "anthropic"), ensuring the wrapper always runs for Anthropic providers. When no explicit betas are configured, an empty array is passed; the wrapper still injects default betas (PI_AI_DEFAULT_ANTHROPIC_BETAS) and OAuth-required betas (PI_AI_OAUTH_ANTHROPIC_BETAS for sk-ant-oat-* tokens) internally.

  • pi-embedded-runner-extraparams.test.ts: Existing tests updated to reflect that default betas are always injected for Anthropic, plus a new test covering the OAuth-without-context1m scenario.

Minor observation: The debug log is only emitted when explicit betas are non-empty. When the wrapper runs with only default/OAuth betas, nothing is logged, which could make runtime diagnostics harder when troubleshooting OAuth failures in production.

Confidence Score: 5/5

  • This PR is safe to merge — it fixes a real OAuth authentication bug with a minimal, well-tested, targeted change.
  • The change is a one-line logic adjustment to remove an incorrect guard condition. The underlying createAnthropicBetaHeadersWrapper already handles the empty-betas case correctly (it always injects PI_AI_DEFAULT_ANTHROPIC_BETAS and PI_AI_OAUTH_ANTHROPIC_BETAS for OAuth), so there is no risk of regression. All 67 existing tests pass and new tests specifically cover the fixed OAuth scenario. The one noted issue is a minor observability gap (missing debug log) that affects diagnostics but not functionality.
  • No files require special attention.

Last reviewed commit: efdb349

Comment on lines +362 to 368
if (provider === "anthropic") {
const betas = anthropicBetas ?? [];
if (betas.length) {
log.debug(`applying Anthropic beta header for ${provider}/${modelId}: ${betas.join(",")}`);
}
agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, betas);
}
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 debug log is only emitted when betas.length > 0. When the wrapper runs with no explicit betas (lines 362–367), nothing is logged, even though the wrapper actively injects PI_AI_DEFAULT_ANTHROPIC_BETAS (or PI_AI_OAUTH_ANTHROPIC_BETAS for OAuth tokens) into headers.

For production debugging—especially with OAuth failures that return HTTP 401—this silent operation could be misleading. Developers troubleshooting may not realize the wrapper actually ran.

Consider logging unconditionally (or at least the effective betas being injected):

Suggested change
if (provider === "anthropic") {
const betas = anthropicBetas ?? [];
if (betas.length) {
log.debug(`applying Anthropic beta header for ${provider}/${modelId}: ${betas.join(",")}`);
}
agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, betas);
}
if (provider === "anthropic") {
const betas = anthropicBetas ?? [];
log.debug(
`applying Anthropic beta headers wrapper for ${provider}/${modelId}` +
(betas.length ? `: configured betas=[${betas.join(",")}]` : " (no explicit betas; defaults/OAuth betas still applied)"),
);
agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, betas);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/extra-params.ts
Line: 362-368

Comment:
The debug log is only emitted when `betas.length > 0`. When the wrapper runs with no explicit betas (lines 362–367), nothing is logged, even though the wrapper actively injects `PI_AI_DEFAULT_ANTHROPIC_BETAS` (or `PI_AI_OAUTH_ANTHROPIC_BETAS` for OAuth tokens) into headers.

For production debugging—especially with OAuth failures that return HTTP 401—this silent operation could be misleading. Developers troubleshooting may not realize the wrapper actually ran.

Consider logging unconditionally (or at least the effective betas being injected):

```suggestion
  if (provider === "anthropic") {
    const betas = anthropicBetas ?? [];
    log.debug(
      `applying Anthropic beta headers wrapper for ${provider}/${modelId}` +
        (betas.length ? `: configured betas=[${betas.join(",")}]` : " (no explicit betas; defaults/OAuth betas still applied)"),
    );
    agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, betas);
  }
```

How can I resolve this? If you propose a fix, please make it concise.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 27, 2026

Codex review: needs changes before merge.

Summary
The PR changes embedded-runner Anthropic extra-param wrapping to always apply the beta-header wrapper and updates embedded-runner tests for default and OAuth beta injection.

Reproducibility: yes. source-backed. Direct Anthropic OAuth with an sk-ant-oat token, a model/options anthropic-beta header, and no context1m or anthropicBeta extra param skips the active wrapper and allows later headers to replace the transport-seeded OAuth beta.

Next step before merge
The source branch is stale/unmergeable against current main and edits obsolete embedded-runner code, but the current-main replacement is narrow and source-backed.

Security
Cleared: The PR only changes Anthropic header-composition logic and tests, with no dependency, workflow, script, package metadata, downloaded-code, or broader secret-access changes.

Review findings

  • [P2] Port the beta wrapper to the active plugin — src/agents/pi-embedded-runner/extra-params.ts:362-368
  • [P2] Keep implicit betas off custom endpoints — src/agents/pi-embedded-runner/extra-params.ts:362-368
Review details

Best possible solution:

Land a narrow replacement in the bundled Anthropic provider plugin that preserves required OAuth/default betas for direct Anthropic OAuth requests even without extra-param betas, keeps custom endpoint suppression, and adds focused regression coverage plus changelog.

Do we have a high-confidence way to reproduce the issue?

Yes, source-backed. Direct Anthropic OAuth with an sk-ant-oat token, a model/options anthropic-beta header, and no context1m or anthropicBeta extra param skips the active wrapper and allows later headers to replace the transport-seeded OAuth beta.

Is this the best way to solve the issue?

No. Always applying beta merging is the right direction for direct Anthropic OAuth, but this PR changes obsolete embedded-runner glue instead of the active Anthropic plugin and does not preserve the custom-endpoint guard required by current docs and tests.

Full review comments:

  • [P2] Port the beta wrapper to the active plugin — src/agents/pi-embedded-runner/extra-params.ts:362-368
    This changes the older embedded-runner guard, but current main composes Anthropic provider wrappers through extensions/anthropic/stream-wrappers.ts. That plugin still skips createAnthropicBetaHeadersWrapper when no extra-param beta is configured, so the reported OAuth plus model-level anthropic-beta path remains broken after this patch.
    Confidence: 0.92
  • [P2] Keep implicit betas off custom endpoints — src/agents/pi-embedded-runner/extra-params.ts:362-368
    The unconditional provider === "anthropic" wrapper path would inject pi-ai default/OAuth betas for every Anthropic-provider request if ported as-is. Current transport/docs suppress implicit Anthropic beta headers on custom-compatible endpoints, so the replacement needs a direct-endpoint guard or equivalent preservation path.
    Confidence: 0.78

Overall correctness: patch is incorrect
Overall confidence: 0.9

Acceptance criteria:

  • pnpm test extensions/anthropic/stream-wrappers.test.ts
  • pnpm test src/agents/anthropic-transport-stream.test.ts
  • pnpm test src/agents/pi-embedded-runner-extraparams.test.ts
  • pnpm check:changed in Testbox before handoff

What I checked:

Likely related people:

  • minupla: Merged fix(anthropic): preserve pi-ai default betas when injecting anthropic-beta header #19789 is the prior fix this PR builds on; the changelog records the OAuth beta preservation behavior and credits @minupla. (role: introduced prior OAuth beta preservation behavior; confidence: medium; commits: 41dfb3813577; files: CHANGELOG.md, src/agents/pi-embedded-runner/extra-params.ts, src/agents/pi-embedded-runner-extraparams.test.ts)
  • vincentkoc: Recent changelog entries credit @vincentkoc on Anthropic/OAuth and Anthropic endpoint-classification work, which overlaps the direct-vs-custom endpoint policy this replacement must preserve. (role: recent adjacent maintainer; confidence: medium; files: extensions/anthropic/stream-wrappers.ts, src/agents/anthropic-transport-stream.ts, docs/concepts/model-providers.md)
  • Peter Steinberger: Local blame for the central Anthropic wrapper and transport lines points to the current checked-out snapshot commit, so this is a weaker routing signal than the feature-specific history above. (role: local snapshot maintainer; confidence: low; commits: 585ce38015ef; files: extensions/anthropic/stream-wrappers.ts, src/agents/anthropic-transport-stream.ts, src/agents/pi-embedded-runner/extra-params.ts)

Remaining risk / open question:

  • Open feat(anthropic): migrate 1M context from beta to GA #45613 changes 1M-context beta semantics, so a replacement should coordinate with that work while still preserving OAuth beta headers when user headers override defaults.
  • No live Anthropic OAuth call was run in this read-only review; the reproduction is source-backed by current header merge order and wrapper composition.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 89a15fddaf84.

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

Labels

agents Agent runtime and tooling size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuth 401 regression: oauth-2025-04-20 beta not injected when context1m set via model headers

1 participant