openclaw - 💡(How to fix) Fix toAnthropicModelRef silently rewrites claude-cli/ model prefix to anthropic/, ignoring explicit provider intent [2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#73660Fetched 2026-04-29 06:16:48
View on GitHub
Comments
2
Participants
3
Timeline
3
Reactions
0
Author
Timeline (top)
commented ×2closed ×1

cli-migration.js:toAnthropicModelRef() unconditionally rewrites any Claude model reference to anthropic/ prefix, even when the user explicitly sets claude-cli/ in their config:

// dist/extensions/anthropic/cli-migration.js lines 7-14
function toAnthropicModelRef(raw) {
    const trimmed = raw.trim();
    const lower = normalizeLowercaseStringOrEmpty(trimmed);
    const provider = lower.startsWith("anthropic/") ? "anthropic"
        : lower.startsWith("claude-cli/") ? CLAUDE_CLI_BACKEND_ID : "";
    if (!provider) return null;
    const modelId = trimmed.slice(provider.length + 1).trim();
    if (!normalizeLowercaseStringOrEmpty(modelId).startsWith("claude-")) return null;
    return \`anthropic/\${modelId}\`;  // <-- always rewrites to anthropic/
}

This function is called by rewriteModelSelection() which processes both primary and fallbacks arrays, and by buildAnthropicCliMigrationResult() which runs on config load when Claude CLI auth is detected.

Root Cause

cli-migration.js:toAnthropicModelRef() unconditionally rewrites any Claude model reference to anthropic/ prefix, even when the user explicitly sets claude-cli/ in their config:

// dist/extensions/anthropic/cli-migration.js lines 7-14
function toAnthropicModelRef(raw) {
    const trimmed = raw.trim();
    const lower = normalizeLowercaseStringOrEmpty(trimmed);
    const provider = lower.startsWith("anthropic/") ? "anthropic"
        : lower.startsWith("claude-cli/") ? CLAUDE_CLI_BACKEND_ID : "";
    if (!provider) return null;
    const modelId = trimmed.slice(provider.length + 1).trim();
    if (!normalizeLowercaseStringOrEmpty(modelId).startsWith("claude-")) return null;
    return \`anthropic/\${modelId}\`;  // <-- always rewrites to anthropic/
}

This function is called by rewriteModelSelection() which processes both primary and fallbacks arrays, and by buildAnthropicCliMigrationResult() which runs on config load when Claude CLI auth is detected.

Code Example

// dist/extensions/anthropic/cli-migration.js lines 7-14
function toAnthropicModelRef(raw) {
    const trimmed = raw.trim();
    const lower = normalizeLowercaseStringOrEmpty(trimmed);
    const provider = lower.startsWith("anthropic/") ? "anthropic"
        : lower.startsWith("claude-cli/") ? CLAUDE_CLI_BACKEND_ID : "";
    if (!provider) return null;
    const modelId = trimmed.slice(provider.length + 1).trim();
    if (!normalizeLowercaseStringOrEmpty(modelId).startsWith("claude-")) return null;
    return \`anthropic/\${modelId}\`;  // <-- always rewrites to anthropic/
}
RAW_BUFFERClick to expand / collapse

Version: 2026.4.25 (6dd1488db3f4)

Description

cli-migration.js:toAnthropicModelRef() unconditionally rewrites any Claude model reference to anthropic/ prefix, even when the user explicitly sets claude-cli/ in their config:

// dist/extensions/anthropic/cli-migration.js lines 7-14
function toAnthropicModelRef(raw) {
    const trimmed = raw.trim();
    const lower = normalizeLowercaseStringOrEmpty(trimmed);
    const provider = lower.startsWith("anthropic/") ? "anthropic"
        : lower.startsWith("claude-cli/") ? CLAUDE_CLI_BACKEND_ID : "";
    if (!provider) return null;
    const modelId = trimmed.slice(provider.length + 1).trim();
    if (!normalizeLowercaseStringOrEmpty(modelId).startsWith("claude-")) return null;
    return \`anthropic/\${modelId}\`;  // <-- always rewrites to anthropic/
}

This function is called by rewriteModelSelection() which processes both primary and fallbacks arrays, and by buildAnthropicCliMigrationResult() which runs on config load when Claude CLI auth is detected.

Steps to reproduce

  1. Set an agent's primary model to claude-cli/claude-opus-4-7 in openclaw.json
  2. Restart the gateway
  3. Open the Agents page in the UI — model still displays as anthropic/claude-opus-4-7

Expected behavior

The config should preserve the explicit claude-cli/ prefix, or the UI should reflect the actual routing provider being used. Users should be able to distinguish between anthropic/ (direct API key) and claude-cli/ (OAuth/Max subscription) in the config and UI.

Actual behavior

The migration function canonicalizes all Claude model refs to anthropic/ regardless of user input. This makes it impossible to verify provider routing from the config or UI, even though the gateway internally routes correctly via agentRuntime.id.

Impact

  • Confusing UX: users set claude-cli/ but the UI always shows anthropic/
  • Config edits appear to be ignored, leading to unnecessary debugging
  • Cannot distinguish auth provider from model config alone

Related

  • #3003 (forced anthropic/ prefix causes issues with non-Anthropic providers)

extent analysis

TL;DR

The toAnthropicModelRef() function should be updated to preserve the user-specified prefix instead of unconditionally rewriting it to anthropic/.

Guidance

  • Review the toAnthropicModelRef() function to ensure it handles the claude-cli/ prefix correctly and preserves user input.
  • Consider adding a conditional statement to check for the claude-cli/ prefix and return the original value if it exists.
  • Verify that the rewriteModelSelection() and buildAnthropicCliMigrationResult() functions are correctly processing the updated model references.
  • Test the changes with different model configurations to ensure the UI reflects the correct provider routing.

Example

function toAnthropicModelRef(raw) {
    const trimmed = raw.trim();
    const lower = normalizeLowercaseStringOrEmpty(trimmed);
    const provider = lower.startsWith("anthropic/") ? "anthropic"
        : lower.startsWith("claude-cli/") ? CLAUDE_CLI_BACKEND_ID : "";
    if (!provider) return null;
    const modelId = trimmed.slice(provider.length + 1).trim();
    if (!normalizeLowercaseStringOrEmpty(modelId).startsWith("claude-")) return null;
    // Preserve the original prefix if it's 'claude-cli/'
    if (lower.startsWith("claude-cli/")) return raw;
    return `anthropic/${modelId}`;
}

Notes

The provided code snippet assumes that the CLAUDE_CLI_BACKEND_ID constant is defined and correctly handles the claude-cli/ prefix. Additional testing and verification may be necessary to ensure the changes do not introduce any regressions.

Recommendation

Apply the workaround by updating the toAnthropicModelRef() function to preserve the user-specified prefix, as shown in the example code snippet. This change will allow users to distinguish between anthropic/ and claude-cli/ providers in the config and

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

The config should preserve the explicit claude-cli/ prefix, or the UI should reflect the actual routing provider being used. Users should be able to distinguish between anthropic/ (direct API key) and claude-cli/ (OAuth/Max subscription) in the config and UI.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING