Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
🐛 OpenRouter: Anthropic models send wrong model ID to API (includes openrouter/ prefix)
Bug Description
When using OpenRouter provider with Anthropic models (e.g., openrouter/anthropic/claude-sonnet-4.6), the model ID sent to OpenRouter API incorrectly includes the openrouter/ prefix.
- OpenClaw internal model ID:
openrouter/anthropic/claude-sonnet-4.6
- Sent to OpenRouter API:
openrouter/anthropic/claude-sonnet-4.6 ❌
- Expected by OpenRouter API:
anthropic/claude-sonnet-4.6 ✅
This causes HTTP 400 errors for all Anthropic models via OpenRouter.
Root Cause
In packages/llm-runtime/src/openai-completions.ts → buildParams(), the model parameter is set directly from model.id:
const params = {
model: model.id, // ← No normalization for OpenRouter
...
};
However, OpenClaw already has the normalization function normalizeOpenRouterModelId() in packages/models/src/extensions/openrouter/models.ts that correctly strips the openrouter/ prefix, but it's not used in the request path.
Affected Code Locations
- Primary fix:
extensions/openrouter/stream.ts → normalizeOpenRouterResolvedModel() hook should normalize model.id
- Alternative fix:
packages/llm-runtime/src/openai-completions.ts → buildParams() should normalize for OpenRouter baseUrl
Reproduction
# Configure OpenRouter with anthropic model
openclaw config set 'agents.defaults.models."openrouter/anthropic/claude-sonnet-4.6"' --json '{"alias":"claude"}'
openclaw gateway restart
# Probe fails
openclaw models status --probe
# → openrouter/anthropic/claude-sonnet-4.6: failed
# Direct curl works (correct model ID)
curl -X POST https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-d '{"model":"anthropic/claude-sonnet-4.6","messages":[{"role":"user","content":"hi"}]}'
# → ✅ Success
Workaround
Use non-Anthropic models on OpenRouter (Google, Meta, Mistral, DeepSeek, etc.) which work correctly because their model IDs don't have the anthropic/ prefix confusion.
Suggested Fix
In normalizeOpenRouterResolvedModel() (extensions/openrouter/stream.ts):
function normalizeOpenRouterResolvedModel(model) {
const normalizedBaseUrl = normalizeOpenRouterBaseUrl(model.baseUrl);
const reasoning = isOpenRouterProxyReasoningUnsupportedModel(model.id) ? false : model.reasoning;
const normalizedId = normalizeOpenRouterModelId(model.id); // Add this
if ((!normalizedBaseUrl || normalizedBaseUrl === model.baseUrl) &&
reasoning === model.reasoning &&
normalizedId === model.id) return;
return {
...model,
...normalizedId ? { id: normalizedId } : {}, // Add this
...normalizedBaseUrl ? { baseUrl: normalizedBaseUrl } : {},
reasoning
};
}
Labels
bug
provider: openrouter
priority: high (affects all Anthropic models on OpenRouter)
Steps to reproduce
Steps to Reproduce
Prerequisites
- OpenClaw gateway running
- OpenRouter API key configured (
OPENROUTER_API_KEY env var)
1. Configure Anthropic model via OpenRouter
# Add model to allowlist
openclaw config set 'agents.defaults.models."openrouter/anthropic/claude-sonnet-4.6"' --json '{"alias":"claude"}'
# Restart gateway to apply
openclaw gateway restart
2. Verify model appears in probe
openclaw models status --probe
Expected (bug):
openrouter/anthropic/claude-sonnet-4.6 ❌ failed · 400 Bad Request
3. Test direct API call (proves OpenRouter works)
curl -X POST https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4.6",
"messages": [{"role": "user", "content": "hi"}],
"max_tokens": 10
}'
Result: ✅ Success (returns completion)
4. Compare with working OpenRouter model
openclaw models status --probe | grep gemini
Result: ✅ openrouter/google/gemini-2.5-flash ok · 1.8s
Minimal Test Script
#!/bin/bash
# test-openrouter-anthropic.sh
echo "=== Testing OpenRouter Anthropic model ==="
openclaw models status --probe | grep claude
echo -e "\n=== Direct API test (correct model ID) ==="
curl -s -X POST https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"anthropic/claude-sonnet-4.6","messages":[{"role":"user","content":"hi"}],"max_tokens":5}' \
| jq -r '.choices[0].message.content // .error.message'
Run: bash test-openrouter-anthropic.sh
Expected behavior
Expected Behavior
Probe Output (After Fix)
$ openclaw models status --probe
┌────────────────────────────────────────────┬────────────────────┬─────────────────────────────────────┐
│ Model │ Profile │ Status │
├────────────────────────────────────────────┼────────────────────┼─────────────────────────────────────┤
│ openrouter/anthropic/claude-sonnet-4.6 │ openrouter:default │ ✅ ok · ~2-3s │
│ openrouter/google/gemini-2.5-flash │ openrouter:default │ ✅ ok · 1.8s │
└────────────────────────────────────────────┴────────────────────┴─────────────────────────────────────┘
API Request Payload (After Fix)
POST https://openrouter.ai/api/v1/chat/completions
{
"model": "anthropic/claude-sonnet-4.6", ← 正確:無 openrouter/ 前綴
"messages": [...],
...
}
User Experience
- Config unchanged: User still uses
openrouter/anthropic/claude-sonnet-4.6 in config
- Routing unchanged: OpenClaw correctly routes to OpenRouter provider
- API call succeeds: OpenRouter receives correct model ID, returns completion
- All Anthropic models work:
claude-3.5-sonnet, claude-3-opus, claude-3-haiku, etc.
Verification Checklist
Actual behavior
Actual Behavior
Probe Output
$ openclaw models status --probe
┌────────────────────────────────────────────┬────────────────────┬─────────────────────────────────────┐
│ Model │ Profile │ Status │
├────────────────────────────────────────────┼────────────────────┼─────────────────────────────────────┤
│ openrouter/anthropic/claude-sonnet-4.6 │ openrouter:default │ ❌ failed · 400 Bad Request │
│ openrouter/google/gemini-2.5-flash │ openrouter:default │ ✅ ok · 1.8s │
└────────────────────────────────────────────┴────────────────────┴─────────────────────────────────────┘
Error Details (from gateway logs)
[openrouter-stream] POST https://openrouter.ai/api/v1/chat/completions 400
{
"error": {
"code": 400,
"message": "Invalid model: openrouter/anthropic/claude-sonnet-4.6.
Available models: anthropic/claude-sonnet-4.6, ...",
"metadata": {
"provider": "openrouter",
"model": "openrouter/anthropic/claude-sonnet-4.6"
}
}
}
Key Evidence
| Aspect |
Value |
| Model ID sent to API |
openrouter/anthropic/claude-sonnet-4.6 ❌ |
| Model ID expected by OpenRouter |
anthropic/claude-sonnet-4.6 ✅ |
| Direct curl with correct ID |
Works perfectly |
| Other OpenRouter models (Google, Meta, etc.) |
Work correctly |
Why Other Models Work
openrouter/google/gemini-2.5-flash → API receives google/gemini-2.5-flash (matches OpenRouter catalog)
openrouter/anthropic/claude-sonnet-4.6 → API receives openrouter/anthropic/claude-sonnet-4.6 (does NOT match catalog)
The bug only affects Anthropic models because their OpenRouter catalog IDs start with anthropic/, which collides with OpenClaw's internal openrouter/ prefix stripping logic.
OpenClaw version
2026.6.6
Operating system
macos 26.3.1
Install method
npm global
Model
openrouter/anthropic/claude-sonnet-4.6
Provider / routing chain
openclaw->openrouter -> claude
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
Impact & Severity
Severity: HIGH 🔴
- User-facing: All Anthropic models via OpenRouter completely broken
- Scope: Affects
claude-sonnet-4.6, claude-3.5-sonnet, claude-3-opus, etc. — all openrouter/anthropic/* models
- Workaround exists: Yes (use Google/Meta/Mistral/DeepSeek on OpenRouter, or direct Anthropic API)
- Data loss: No
- Security: No
Affected Users
| User Segment |
Impact |
| OpenClaw users with OpenRouter + Anthropic models |
Cannot use any Anthropic model via OpenRouter |
Users relying on claude alias for coding/reasoning |
Forced to switch models or providers |
| New users trying OpenRouter Anthropic models |
Immediate 400 error, bad first impression |
Business/Product Impact
- Model diversity reduced: OpenRouter's main value prop (access to all providers) partially broken
- Claude 4.6 Sonnet is a flagship model — unavailable via OpenRouter in OpenClaw
- Workaround friction: Users must reconfigure to direct Anthropic API (needs separate API key, different rate limits, no OpenRouter routing features)
Technical Debt
- Function exists but unused:
normalizeOpenRouterModelId() written, tested, but not hooked into request path
- Inconsistent behavior: Google/Meta/Mistral models work on OpenRouter, only Anthropic fails
- Silent failure: Probe shows generic "failed · 400", no hint about model ID format issue
Severity Justification
| Criterion |
Rating |
| User blocking |
🔴 High (complete model category unavailable) |
| Workaround ease |
🟡 Medium (config change, different API key) |
| Fix complexity |
🟢 Low (~3 lines in correct hook) |
| Regression risk |
🟢 Low (normalization is idempotent) |
Overall: HIGH severity, LOW fix effort — ideal quick win.
Additional information
No response
Bug type
Regression (worked before, now fails)
Beta release blocker
No
Summary
🐛 OpenRouter: Anthropic models send wrong model ID to API (includes
openrouter/prefix)Bug Description
When using OpenRouter provider with Anthropic models (e.g.,
openrouter/anthropic/claude-sonnet-4.6), the model ID sent to OpenRouter API incorrectly includes theopenrouter/prefix.openrouter/anthropic/claude-sonnet-4.6openrouter/anthropic/claude-sonnet-4.6❌anthropic/claude-sonnet-4.6✅This causes HTTP 400 errors for all Anthropic models via OpenRouter.
Root Cause
In
packages/llm-runtime/src/openai-completions.ts→buildParams(), themodelparameter is set directly frommodel.id:However, OpenClaw already has the normalization function
normalizeOpenRouterModelId()inpackages/models/src/extensions/openrouter/models.tsthat correctly strips theopenrouter/prefix, but it's not used in the request path.Affected Code Locations
extensions/openrouter/stream.ts→normalizeOpenRouterResolvedModel()hook should normalizemodel.idpackages/llm-runtime/src/openai-completions.ts→buildParams()should normalize for OpenRouter baseUrlReproduction
Workaround
Use non-Anthropic models on OpenRouter (Google, Meta, Mistral, DeepSeek, etc.) which work correctly because their model IDs don't have the
anthropic/prefix confusion.Suggested Fix
In
normalizeOpenRouterResolvedModel()(extensions/openrouter/stream.ts):Labels
bugprovider: openrouterpriority: high(affects all Anthropic models on OpenRouter)Steps to reproduce
Steps to Reproduce
Prerequisites
OPENROUTER_API_KEYenv var)1. Configure Anthropic model via OpenRouter
2. Verify model appears in probe
Expected (bug):
3. Test direct API call (proves OpenRouter works)
Result: ✅ Success (returns completion)
4. Compare with working OpenRouter model
openclaw models status --probe | grep geminiResult: ✅
openrouter/google/gemini-2.5-flash ok · 1.8sMinimal Test Script
Run:
bash test-openrouter-anthropic.shExpected behavior
Expected Behavior
Probe Output (After Fix)
$ openclaw models status --probe ┌────────────────────────────────────────────┬────────────────────┬─────────────────────────────────────┐ │ Model │ Profile │ Status │ ├────────────────────────────────────────────┼────────────────────┼─────────────────────────────────────┤ │ openrouter/anthropic/claude-sonnet-4.6 │ openrouter:default │ ✅ ok · ~2-3s │ │ openrouter/google/gemini-2.5-flash │ openrouter:default │ ✅ ok · 1.8s │ └────────────────────────────────────────────┴────────────────────┴─────────────────────────────────────┘API Request Payload (After Fix)
User Experience
openrouter/anthropic/claude-sonnet-4.6in configclaude-3.5-sonnet,claude-3-opus,claude-3-haiku, etc.Verification Checklist
openclaw models status --probeshows ✅ for allopenrouter/anthropic/*modelsanthropic/claude-*) still works independentlyActual behavior
Actual Behavior
Probe Output
Error Details (from gateway logs)
Key Evidence
openrouter/anthropic/claude-sonnet-4.6❌anthropic/claude-sonnet-4.6✅Why Other Models Work
openrouter/google/gemini-2.5-flash→ API receivesgoogle/gemini-2.5-flash(matches OpenRouter catalog)openrouter/anthropic/claude-sonnet-4.6→ API receivesopenrouter/anthropic/claude-sonnet-4.6(does NOT match catalog)The bug only affects Anthropic models because their OpenRouter catalog IDs start with
anthropic/, which collides with OpenClaw's internalopenrouter/prefix stripping logic.OpenClaw version
2026.6.6
Operating system
macos 26.3.1
Install method
npm global
Model
openrouter/anthropic/claude-sonnet-4.6
Provider / routing chain
openclaw->openrouter -> claude
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
Impact & Severity
Severity: HIGH 🔴
claude-sonnet-4.6,claude-3.5-sonnet,claude-3-opus, etc. — allopenrouter/anthropic/*modelsAffected Users
claudealias for coding/reasoningBusiness/Product Impact
Technical Debt
normalizeOpenRouterModelId()written, tested, but not hooked into request pathSeverity Justification
Overall: HIGH severity, LOW fix effort — ideal quick win.
Additional information
No response