fix(anthropic): honor ANTHROPIC_BASE_URL when no baseUrl is configured#74432
fix(anthropic): honor ANTHROPIC_BASE_URL when no baseUrl is configured#74432sunapi386 wants to merge 2 commits into
Conversation
Two helpers in src/agents fall back to a hardcoded "https://api.anthropic.com" when no baseUrl is supplied — the messages stream URL builder (anthropic-transport-stream.ts:474) and the native-PDF tool fetch (tools/pdf-native-providers.ts:65). Both silently ignore ANTHROPIC_BASE_URL, so users running OpenClaw behind an Anthropic-compatible proxy see calls hit api.anthropic.com directly even when the env var is set. The fix adds env-var fallback to the existing precedence chain: explicit baseUrl > ANTHROPIC_BASE_URL > "https://api.anthropic.com". Mirrors what the Anthropic Node SDK does and matches the OpenAI counterpart PR (openclaw#74427). Exports resolveAnthropicMessagesUrl from anthropic-transport-stream.ts so the precedence is unit-testable; the inline pdf-native-providers fix uses the same chain. Backwards-compat: explicit baseUrl still wins, so users with config-set or runtime-passed baseUrl see no change.
Greptile SummaryThis PR adds Confidence Score: 3/5Not safe to merge as-is — whitespace-only ANTHROPIC_BASE_URL will cause silent fetch failures in anthropicAnalyzePdf. One P1 logic bug in pdf-native-providers.ts where ?? vs || causes a whitespace env var to produce an empty base URL. The fix is a one-line change. The rest of the PR (stream transport, tests) is correct and well-covered. src/agents/tools/pdf-native-providers.ts — ?? operator bug on lines 65–69 Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/agents/tools/pdf-native-providers.ts
Line: 65-69
Comment:
**Whitespace-only `ANTHROPIC_BASE_URL` bypasses the default fallback**
`??` only guards against `null`/`undefined`, not empty strings. When `ANTHROPIC_BASE_URL` is set to whitespace (e.g. `" "`), `process.env.ANTHROPIC_BASE_URL?.trim()` returns `""` — which is not nullish — so `"" ?? "https://api.anthropic.com"` evaluates to `""`. The resulting `fetch` URL becomes `"/v1/messages"` (a relative path), which will fail at runtime.
`resolveAnthropicMessagesUrl` handles this correctly with `||` (falsy check), but this site uses `??`. The fix is to match the same pattern:
```suggestion
const baseUrl = (
params.baseUrl ||
process.env.ANTHROPIC_BASE_URL?.trim() ||
"https://api.anthropic.com"
).replace(/\/+$/, "");
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/agents/tools/pdf-native-providers.ts
Line: 70
Comment:
**`/v1` suffix not handled, unlike `resolveAnthropicMessagesUrl`**
`resolveAnthropicMessagesUrl` checks `normalized.endsWith("/v1")` and avoids doubling the segment. This site always appends `/v1/messages`, so if `ANTHROPIC_BASE_URL` is `"https://proxy.example.com/v1"`, the call will go to `/v1/v1/messages`. Pre-existing issue, but now surfaced by the env-var path being live. Consider extracting the shared URL-building logic into `resolveAnthropicMessagesUrl` (or a sibling) and reusing it here.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(anthropic): honor ANTHROPIC_BASE_URL..." | Re-trigger Greptile |
| const baseUrl = ( | ||
| params.baseUrl ?? | ||
| process.env.ANTHROPIC_BASE_URL?.trim() ?? | ||
| "https://api.anthropic.com" | ||
| ).replace(/\/+$/, ""); |
There was a problem hiding this comment.
Whitespace-only
ANTHROPIC_BASE_URL bypasses the default fallback
?? only guards against null/undefined, not empty strings. When ANTHROPIC_BASE_URL is set to whitespace (e.g. " "), process.env.ANTHROPIC_BASE_URL?.trim() returns "" — which is not nullish — so "" ?? "https://api.anthropic.com" evaluates to "". The resulting fetch URL becomes "/v1/messages" (a relative path), which will fail at runtime.
resolveAnthropicMessagesUrl handles this correctly with || (falsy check), but this site uses ??. The fix is to match the same pattern:
| const baseUrl = ( | |
| params.baseUrl ?? | |
| process.env.ANTHROPIC_BASE_URL?.trim() ?? | |
| "https://api.anthropic.com" | |
| ).replace(/\/+$/, ""); | |
| const baseUrl = ( | |
| params.baseUrl || | |
| process.env.ANTHROPIC_BASE_URL?.trim() || | |
| "https://api.anthropic.com" | |
| ).replace(/\/+$/, ""); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/pdf-native-providers.ts
Line: 65-69
Comment:
**Whitespace-only `ANTHROPIC_BASE_URL` bypasses the default fallback**
`??` only guards against `null`/`undefined`, not empty strings. When `ANTHROPIC_BASE_URL` is set to whitespace (e.g. `" "`), `process.env.ANTHROPIC_BASE_URL?.trim()` returns `""` — which is not nullish — so `"" ?? "https://api.anthropic.com"` evaluates to `""`. The resulting `fetch` URL becomes `"/v1/messages"` (a relative path), which will fail at runtime.
`resolveAnthropicMessagesUrl` handles this correctly with `||` (falsy check), but this site uses `??`. The fix is to match the same pattern:
```suggestion
const baseUrl = (
params.baseUrl ||
process.env.ANTHROPIC_BASE_URL?.trim() ||
"https://api.anthropic.com"
).replace(/\/+$/, "");
```
How can I resolve this? If you propose a fix, please make it concise.| process.env.ANTHROPIC_BASE_URL?.trim() ?? | ||
| "https://api.anthropic.com" | ||
| ).replace(/\/+$/, ""); | ||
| const res = await fetch(`${baseUrl}/v1/messages`, { |
There was a problem hiding this comment.
/v1 suffix not handled, unlike resolveAnthropicMessagesUrl
resolveAnthropicMessagesUrl checks normalized.endsWith("/v1") and avoids doubling the segment. This site always appends /v1/messages, so if ANTHROPIC_BASE_URL is "https://proxy.example.com/v1", the call will go to /v1/v1/messages. Pre-existing issue, but now surfaced by the env-var path being live. Consider extracting the shared URL-building logic into resolveAnthropicMessagesUrl (or a sibling) and reusing it here.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/pdf-native-providers.ts
Line: 70
Comment:
**`/v1` suffix not handled, unlike `resolveAnthropicMessagesUrl`**
`resolveAnthropicMessagesUrl` checks `normalized.endsWith("/v1")` and avoids doubling the segment. This site always appends `/v1/messages`, so if `ANTHROPIC_BASE_URL` is `"https://proxy.example.com/v1"`, the call will go to `/v1/v1/messages`. Pre-existing issue, but now surfaced by the env-var path being live. Consider extracting the shared URL-building logic into `resolveAnthropicMessagesUrl` (or a sibling) and reusing it here.
How can I resolve this? If you propose a fix, please make it concise.|
Codex review: needs real behavior proof before merge. Reviewed May 30, 2026, 12:57 AM ET / 04:57 UTC. Summary PR surface: Source +16, Tests +49. Total +65 across 3 files. Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Review detailsBest possible solution: Retry the Codex review after fixing the execution failure. Do we have a high-confidence way to reproduce the issue? Unclear. The review failed before ClawSweeper could establish a reproduction path. Is this the best way to solve the issue? Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction. AGENTS.md: unclear because the file could not be read completely. Codex review notes: model gpt-5.5, reasoning high; reviewed against b352cb2d8e7f. Label changesLabel justifications:
Evidence reviewedPR surface: Source +16, Tests +49. Total +65 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Greptile flagged two issues with the inline URL build: P1 (real bug): the chain used `??` instead of `||`, so a whitespace-only ANTHROPIC_BASE_URL trimmed to "" passed nullish-checks and produced a relative `/v1/messages` URL that fetch() can't resolve. P2 (pre-existing): the inline path always appended `/v1/messages`, so a proxy URL ending in `/v1` (common with LiteLLM) would hit `/v1/v1/messages`. Both fixed by reusing the canonical resolveAnthropicMessagesUrl helper — single precedence chain, single `/v1` suffix logic, no duplication.
|
Codex review: needs changes before merge. What this changes: The PR exports an Anthropic Messages URL resolver that falls back from explicit Required change before merge: This is a narrow, valid provider-routing PR with a concrete remaining repair: add PDF-specific regression coverage and, if desired, extract the resolver to a small shared helper. No product, release, or security-policy decision is required before attempting that repair. Review detailsBest possible solution: Land the provider-routing fix after adding native PDF regression tests and confirming the shared URL helper is in an acceptable module boundary. The shipped behavior should preserve explicit Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 8935dd154a95. |
|
This pull request has been automatically marked as stale due to inactivity. |
Repairs a batch of narrow model/provider edge cases: - honor OpenAI and Anthropic base URL environment overrides when provider config does not set an explicit base URL - preserve OpenRouter Anthropic cache retention while stripping unsupported transport options - allow apply_patch for non-OpenAI providers when the tool config otherwise permits it - prune stale same-provider model selections from configure/model picker state - expose GitHub Copilot bundled thinking policy metadata to offline/provider-policy lookups - repair additive SQLite shared-state upgrades for existing databases - keep same-size rotated log readers from reusing stale content in CI tooling Proof: - GitHub PR checks green on exact head 4651490 - Crabbox delegated Blacksmith Testbox tbx_01kt3em5r9vd7g0bnykrff6jdk exited 0 - Focused local Vitest/oxlint/format proof recorded in PR body and land-ready comment Fixes #80347. Fixes #88357. Fixes #45269. Supersedes #74427, #74432, #79370, #79894, #80366, and #88359.
Repairs a batch of narrow model/provider edge cases: - honor OpenAI and Anthropic base URL environment overrides when provider config does not set an explicit base URL - preserve OpenRouter Anthropic cache retention while stripping unsupported transport options - allow apply_patch for non-OpenAI providers when the tool config otherwise permits it - prune stale same-provider model selections from configure/model picker state - expose GitHub Copilot bundled thinking policy metadata to offline/provider-policy lookups - repair additive SQLite shared-state upgrades for existing databases - keep same-size rotated log readers from reusing stale content in CI tooling Proof: - GitHub PR checks green on exact head 4651490 - Crabbox delegated Blacksmith Testbox tbx_01kt3em5r9vd7g0bnykrff6jdk exited 0 - Focused local Vitest/oxlint/format proof recorded in PR body and land-ready comment Fixes openclaw#80347. Fixes openclaw#88357. Fixes openclaw#45269. Supersedes openclaw#74427, openclaw#74432, openclaw#79370, openclaw#79894, openclaw#80366, and openclaw#88359.
Repairs a batch of narrow model/provider edge cases: - honor OpenAI and Anthropic base URL environment overrides when provider config does not set an explicit base URL - preserve OpenRouter Anthropic cache retention while stripping unsupported transport options - allow apply_patch for non-OpenAI providers when the tool config otherwise permits it - prune stale same-provider model selections from configure/model picker state - expose GitHub Copilot bundled thinking policy metadata to offline/provider-policy lookups - repair additive SQLite shared-state upgrades for existing databases - keep same-size rotated log readers from reusing stale content in CI tooling Proof: - GitHub PR checks green on exact head 4651490 - Crabbox delegated Blacksmith Testbox tbx_01kt3em5r9vd7g0bnykrff6jdk exited 0 - Focused local Vitest/oxlint/format proof recorded in PR body and land-ready comment Fixes openclaw#80347. Fixes openclaw#88357. Fixes openclaw#45269. Supersedes openclaw#74427, openclaw#74432, openclaw#79370, openclaw#79894, openclaw#80366, and openclaw#88359.
Repairs a batch of narrow model/provider edge cases: - honor OpenAI and Anthropic base URL environment overrides when provider config does not set an explicit base URL - preserve OpenRouter Anthropic cache retention while stripping unsupported transport options - allow apply_patch for non-OpenAI providers when the tool config otherwise permits it - prune stale same-provider model selections from configure/model picker state - expose GitHub Copilot bundled thinking policy metadata to offline/provider-policy lookups - repair additive SQLite shared-state upgrades for existing databases - keep same-size rotated log readers from reusing stale content in CI tooling Proof: - GitHub PR checks green on exact head 4651490 - Crabbox delegated Blacksmith Testbox tbx_01kt3em5r9vd7g0bnykrff6jdk exited 0 - Focused local Vitest/oxlint/format proof recorded in PR body and land-ready comment Fixes openclaw#80347. Fixes openclaw#88357. Fixes openclaw#45269. Supersedes openclaw#74427, openclaw#74432, openclaw#79370, openclaw#79894, openclaw#80366, and openclaw#88359.
Summary
Two helpers in
src/agentsfall back to a hardcodedhttps://api.anthropic.comwhen nobaseUrlis supplied:resolveAnthropicMessagesUrlinsrc/agents/anthropic-transport-stream.ts:474— used by every streaming Anthropic callsrc/agents/tools/pdf-native-providers.ts:65— used by Anthropic's PDF document toolBoth silently ignore
ANTHROPIC_BASE_URL, so users running OpenClaw behind an Anthropic-compatible proxy (LiteLLM, vLLM, local gateway) see calls hitapi.anthropic.comdirectly even when the env var is set.The fix adds
ANTHROPIC_BASE_URLto the existing precedence chain:This mirrors what the Anthropic Node SDK already does and matches the OpenAI counterpart PR (#74427) and the merged whisper precedent (#55597).
Backwards compatibility
Explicit
baseUrlstill takes precedence:model.baseUrlset (e.g. via provider config)ANTHROPIC_BASE_URLenv varTest plan
resolveAnthropicMessagesUrlso the precedence chain is unit-testable.anthropic-transport-stream.test.tscovering: explicit wins over env, env fallback for empty/whitespace baseUrl, default fallback when neither is set.pnpm test -- src/agents/anthropic-transport-stream.test.ts→ 25/25 pass (21 existing + 4 new).Related