openclaw - ✅(Solved) Fix [Bug]: --model anthropic/CLAUDE-OPUS-4-7 (case-mismatched model name) is accepted by CLI catalog and dispatched to the provider, surfacing as misleading "No text output returned" — provider name is case-insensitive but model name is not [1 pull requests, 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#73715Fetched 2026-04-29 06:16:03
View on GitHub
Comments
2
Participants
3
Timeline
7
Reactions
0
Author
Timeline (top)
commented ×2cross-referenced ×2labeled ×1mentioned ×1

pnpm openclaw infer model run --model <provider>/<model> accepts the provider part case-insensitively (anthropic, Anthropic, ANTHROPIC all resolve to the same provider) but treats the model part case-sensitively at the provider call. The CLI catalog lookup does not reject case-mismatched model names; the request goes through to the provider with the mismatched id, which fails (or returns no content). The user sees Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7". — the same misleading "No text output returned" wording from #73185 — when the actual cause is "model id case mismatch." Either model lookup should be case-insensitive (matching provider behavior), or the CLI should reject case-mismatched model ids upfront with a clear "Unknown model" error.

Error Message

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK" Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7". ELIFECYCLE Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/Claude-Opus-4-7 --prompt "Just say OK" Error: No text output returned for provider "anthropic" model "Claude-Opus-4-7". ELIFECYCLE Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK" --json Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7". ELIFECYCLE Command failed with exit code 1.

Root Cause

The error message reports the exact case the user typed — proving the model id was passed through to the provider unchanged. Note that the "No text output returned" wording is the same one #73185 patches — that issue is about empty --prompt; this one is about case-mismatched --model. Both have the same downstream symptom for the same root cause: input not normalized/validated before dispatch.

Fix Action

Fix / Workaround

  1. Make model lookup case-insensitive too. --model anthropic/CLAUDE-OPUS-4-7 should resolve to the same model as --model anthropic/claude-opus-4-7 (the catalog already does this for the provider half) and dispatch the canonical (lowercase) id to the provider. Result: both succeed.
  2. Make model lookup case-sensitive AND normalize the catalog rejection. --model anthropic/CLAUDE-OPUS-4-7 should fail with Error: Unknown model: anthropic/CLAUDE-OPUS-4-7 (mirroring how anthropic/claude-fake-9000 fails today), with no provider call. Optionally include a "did you mean: anthropic/claude-opus-4-7?" hint when a case-only difference is detected.

The error message reports the exact case the user typed — proving the model id was passed through to the provider unchanged. Note that the "No text output returned" wording is the same one #73185 patches — that issue is about empty --prompt; this one is about case-mismatched --model. Both have the same downstream symptom for the same root cause: input not normalized/validated before dispatch.

  • Likely fix locus: the model-id normalization/lookup path (search for "Unknown model:" in src/cli/cli-infer/ or wherever the parser lives in this build, plus the model-catalog resolution helper). Two viable approaches:
    1. Match provider behavior — make model lookup case-insensitive, normalize the matched canonical id, and dispatch the canonical id to the provider. Most user-friendly.
    2. Make model lookup case-sensitive AND route case-mismatched ids through the existing "Unknown model" rejection path. Most consistent with literal id semantics. Optionally add a "did you mean: ...?" hint.

PR fix notes

PR #73717: fix(cli): canonicalize infer model run refs

Description (problem / solution / changelog)

Summary

  • Problem: infer model run --model forwarded case-mismatched catalog model ids such as Anthropic/CLAUDE-OPUS-4-7 to the model runtime, which could surface as a misleading empty-output provider error.
  • Why it matters: users can paste natural mixed-case model refs and get a provider-looking failure instead of the canonical known model behavior.
  • What changed: explicit model-run overrides now resolve through the model catalog when there is a unique case-insensitive provider/model match, then dispatch the canonical provider/model ref for both local and gateway transports.
  • What did NOT change (scope boundary): custom mixed-case refs are preserved when no unique catalog match exists; this does not change model catalog listing, provider auth, or non-infer model run commands.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #73715
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: infer model run parsed and forwarded explicit model overrides without canonicalizing case-only matches against the known model catalog.
  • Missing detection / guardrail: no regression test covered case-mismatched explicit model refs before local or gateway dispatch.
  • Contributing context (if known): provider ids are normalized case-insensitively, while model ids were forwarded with user-supplied casing.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/cli/capability-cli.test.ts
  • Scenario the test should lock in: Anthropic/CLAUDE-OPUS-4-7 is canonicalized to anthropic/claude-opus-4-7 before local and gateway model-run dispatch.
  • Why this is the smallest reliable guardrail: the bug is in the CLI override-to-dispatch seam, so mocked dispatch tests catch the raw value before any provider call.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

openclaw infer model run --model <provider/model> now accepts case-only mismatches for catalog models when the match is unique, dispatching the canonical model id instead of the raw casing.

Diagram (if applicable)

Before:
--model Anthropic/CLAUDE-OPUS-4-7 -> raw runtime dispatch -> misleading provider/no-output error

After:
--model Anthropic/CLAUDE-OPUS-4-7 -> unique catalog match -> anthropic/claude-opus-4-7 -> normal dispatch

Security Impact (required)

  • New permissions/capabilities? (Yes/No): No
  • Secrets/tokens handling changed? (Yes/No): No
  • New/changed network calls? (Yes/No): No
  • Command/tool execution surface changed? (Yes/No): No
  • Data access scope changed? (Yes/No): No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Ubuntu 24.04.4 LTS
  • Runtime/container: Node 22, pnpm dev checkout
  • Model/provider: Anthropic catalog model refs
  • Integration/channel (if any): CLI infer model run
  • Relevant config (redacted): default source-checkout runtime config; no secrets included

Steps

  1. Run openclaw infer model run --model Anthropic/CLAUDE-OPUS-4-7 --prompt "hello" --json.
  2. Observe the model-run dispatch receives the canonical ref.
  3. Run the same path with a custom mixed-case ref that has no catalog match.

Expected

  • Known catalog model refs with case-only mismatches dispatch as their canonical provider/model id.
  • Custom mixed-case refs without a unique catalog match remain unchanged.

Actual

  • Before this PR, the raw mixed-case catalog model id was forwarded.
  • After this PR, the CLI canonicalizes only unique catalog matches before dispatch.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)
pnpm test src/cli/capability-cli.test.ts -- --reporter=verbose
Test Files  1 passed (1)
Tests  47 passed (47)

pnpm check:changed
passed

git diff --check
passed

Human Verification (required)

  • Verified scenarios: local dispatch canonicalization, gateway dispatch canonicalization, custom mixed-case ref preservation.

  • Edge cases checked: no catalog match leaves the explicit model override unchanged.

  • What you did not verify: live Anthropic provider call.

  • pnpm test src/cli/capability-cli.test.ts -- --reporter=verbose passed: 51 tests.

  • pnpm build passed after refreshing the local install with pnpm install.

  • Live CLI smoke with existing local OpenRouter auth profile passed:

    • Command shape: pnpm --silent openclaw infer model run --model OpenRouter/OPENROUTER/AUTO --prompt 'Reply with OK only.' --json
    • Result: exit 0, parseable JSON, ok=true, provider=openrouter, model=openrouter/auto, output text OK.
  • Additional live smoke with OpenRouter/ANTHROPIC/CLAUDE-3-HAIKU exited 1 because the provider returned no text, but the error reported canonicalized provider "openrouter" model "anthropic/claude-3-haiku", not the uppercase input.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No): Yes
  • Config/env changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: A custom provider intentionally using mixed-case ids could be changed unexpectedly.
    • Mitigation: canonicalization only happens when the loaded model catalog has exactly one case-insensitive provider/model match; otherwise the raw ref is preserved.

Changed files

  • src/cli/capability-cli.test.ts (modified, +110/-0)
  • src/cli/capability-cli.ts (modified, +64/-5)

Code Example

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK"
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/Claude-Opus-4-7 --prompt "Just say OK"
Error: No text output returned for provider "anthropic" model "Claude-Opus-4-7".
 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK" --json
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

---

- Default model: anthropic/claude-opus-4-7 (set via `pnpm openclaw models set anthropic/claude-opus-4-7`).
- Anthropic auth: Claude CLI OAuth (detected by `openclaw doctor`).
- No per-agent overrides; default agent `main`; gateway `mode=local`.
- The bug reproduces under both `--local` (default in this run) and `--gateway` transports — this is upstream of transport selection, in the model-id normalization path.

---

=== Verified case-sensitivity matrix (single shell session, OpenClaw 2026.4.27 / 0450bba, Node 22.22.2) ===

EXIT  MODEL ID                                       RESULT
0     anthropic/claude-opus-4-7                      outputs: 1                                      (canonical case)
0     Anthropic/claude-opus-4-7                      outputs: 1                                      (provider uppercase OK)
0     ANTHROPIC/claude-opus-4-7                      outputs: 1                                      (provider full uppercase OK)
1     anthropic/Claude-Opus-4-7                      Error: No text output returned for provider "anthropic" model "Claude-Opus-4-7"
1     anthropic/CLAUDE-OPUS-4-7                      Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7"
1     Anthropic/CLAUDE-OPUS-4-7                      Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7"

=== The bug: case-mismatched model name reaches the provider ===

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK"
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK" --json
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

=== In-tree counter-example #1: provider name is case-insensitive ===

$ pnpm openclaw infer model run --model Anthropic/claude-opus-4-7 --prompt "Just say OK" --json | jq -r '.outputs[0].text'
OK

$ pnpm openclaw infer model run --model ANTHROPIC/claude-opus-4-7 --prompt "Just say OK" --json | jq -r '.outputs[0].text'
OK

=== In-tree counter-example #2: nonexistent model is rejected upfront ===

$ pnpm openclaw infer model run --model anthropic/claude-fake-9000 --prompt "Just say OK"
Error: Unknown model: anthropic/claude-fake-9000
 ELIFECYCLE  Command failed with exit code 1.

(Note: this rejection happens BEFORE any provider call. The Unknown-model code path exists; case-mismatched model ids should take the same path.)

=== Sanity check: the canonical lowercase model id works correctly ===

$ pnpm openclaw infer model run --model anthropic/claude-opus-4-7 --prompt "Just say OK" --json | jq -r '.outputs[0].text'
OK

---

Affected users/systems/channels:
- Every operator who pastes a model id with mixed case (e.g. copy-paste from docs or chat where headers are title-cased) into `infer model run --model`. Linux directly observed (Ubuntu 24.04 / Node 22.22.2 / pnpm 10.33.0); platform-agnostic code path so macOS/Windows are expected to reproduce.
- New users who don't know the canonical lowercase form of model ids and try natural-feeling capitalizations (`Claude-Opus-4-7`, `CLAUDE-OPUS-4-7`).
- Users hitting this on Anthropic specifically; whether other providers exhibit the same asymmetry depends on each provider plugin's normalization (not directly verified for non-anthropic providers).

Severity:
- Annoying with confusing diagnostics. The "No text output returned" error misattributes a CLI input mistake to a provider/model fault, sending users down the wrong debugging path.
- For non-Claude providers that bill on partial requests: the case-mismatched call may incur tokens depending on whether the provider rejects pre-call or accepts and returns empty (not directly verified for non-anthropic providers).
- Inconsistent with the in-tree pattern. Provider name is case-insensitive; model name is not. Same input field, two case-sensitivity rules.
- Not a security/data-loss bug, no crash.

Frequency:
- Always, deterministic for case-mismatched anthropic model names. 100% reproduction across `Claude-Opus-4-7`, `CLAUDE-OPUS-4-7`, and `Anthropic/CLAUDE-OPUS-4-7`. Independent of model/transport.

Consequence:
- Operators waste time debugging "why doesn't claude work?" when the real issue is case mismatch in the model id.
- Inconsistency erodes operator trust in input parsing — the CLI accepts case-insensitive provider but rejects (downstream, with a misleading error) case-mismatched model.
- The "No text output returned" error string is now overloaded across at least three failure modes: empty prompt (#73185), case-mismatched model (this), and a configured provider returning empty body for valid input (closed #65394, #66506, #65076). Every overload makes the error less useful.
- No grounded evidence of missed messages, failed onboarding, or extra cost.

---

- Regression status: not classified as a Regression. Last-known-good not directly observed; no bisect performed.

- Likely fix locus: the model-id normalization/lookup path (search for "Unknown model:" in `src/cli/cli-infer/` or wherever the parser lives in this build, plus the model-catalog resolution helper). Two viable approaches:
  1. Match provider behavior — make model lookup case-insensitive, normalize the matched canonical id, and dispatch the canonical id to the provider. Most user-friendly.
  2. Make model lookup case-sensitive AND route case-mismatched ids through the existing "Unknown model" rejection path. Most consistent with literal id semantics. Optionally add a "did you mean: ...?" hint.

- Suggested regression test: a unit test on the model-id resolver that asserts each of `Anthropic/claude-opus-4-7`, `anthropic/CLAUDE-OPUS-4-7`, `anthropic/Claude-Opus-4-7`, `Anthropic/CLAUDE-OPUS-4-7` either (a) resolves to the canonical `anthropic/claude-opus-4-7` and dispatches accordingly, OR (b) is rejected with `Error: Unknown model: <input>`. Pair with an existing-behavior parity test that the canonical lowercase id still works.

- Related findings:
  - #73185 (filed): empty `--prompt` reaches the provider on `--local` transport. Same misleading downstream error string ("No text output returned"), different upstream cause (empty content vs case-mismatched model). Same root-cause class: the CLI doesn't validate/normalize input before dispatching.
  - Closed issues #65394, #66506, #65076: addressed "No text output returned" in valid-input scenarios. The same string remains overloaded across at least three failure modes; this issue makes that overload one mode larger.
  - Related parsing edge case: `--model anthropic/` (provider with trailing slash, no model) currently resolves to the provider's default model and succeeds — undocumented hidden behavior. Could be filed separately if maintainers consider it a UX gap.
  - Related parsing edge case: `--model anthropic` (provider only, no slash) currently fails with `Error: Unknown model: anthropic/anthropic` — implicit `<provider>/<provider>` auto-prefix exposes internal logic in the error message. Could be filed separately.

- Dedupe checked against the openclaw issue corpus on 2026-04-28: no existing open or closed issue matches the case-sensitivity asymmetry pattern specifically. Searched "case sensitive model id", "model id case mismatch", "case insensitive provider model", "uppercase model name infer" — zero direct matches.

- Not exercised in this repro: case-sensitivity behavior for non-anthropic providers (deepseek, openrouter, etc.); behavior under `--gateway` transport (the bug is in the model-id resolver upstream of transport, but not directly verified on gateway path); aliases with case mismatches (e.g. `Opus` vs `opus`).
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

pnpm openclaw infer model run --model <provider>/<model> accepts the provider part case-insensitively (anthropic, Anthropic, ANTHROPIC all resolve to the same provider) but treats the model part case-sensitively at the provider call. The CLI catalog lookup does not reject case-mismatched model names; the request goes through to the provider with the mismatched id, which fails (or returns no content). The user sees Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7". — the same misleading "No text output returned" wording from #73185 — when the actual cause is "model id case mismatch." Either model lookup should be case-insensitive (matching provider behavior), or the CLI should reject case-mismatched model ids upfront with a clear "Unknown model" error.

Steps to reproduce

  1. Fresh checkout of openclaw at v2026.4.27 (commit 0450bba); pnpm install && pnpm build on Node 22.22.2.
  2. With anthropic auth configured (Claude CLI OAuth in this run).
  3. Run pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK".
  4. Observe Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7". and exit 1. The catalog accepted the case-mismatched id; the provider call ran and failed; the error misattributes the cause.
  5. Run with mixed case in the model: --model anthropic/Claude-Opus-4-7. Same misleading error.
  6. Counter-example for the provider half of the id: --model Anthropic/claude-opus-4-7, --model ANTHROPIC/claude-opus-4-7. Both succeed with exit 0 — provider name is case-insensitive.
  7. Counter-example for an actually-unknown model: --model anthropic/claude-fake-9000. CLI rejects upfront with Error: Unknown model: anthropic/claude-fake-9000 and exit 1, before any provider call. The catalog has the correct error path; it just doesn't take the case-mismatched route.

Expected behavior

One of the following — either one fixes the bug, pick whichever matches the project's case convention:

  1. Make model lookup case-insensitive too. --model anthropic/CLAUDE-OPUS-4-7 should resolve to the same model as --model anthropic/claude-opus-4-7 (the catalog already does this for the provider half) and dispatch the canonical (lowercase) id to the provider. Result: both succeed.
  2. Make model lookup case-sensitive AND normalize the catalog rejection. --model anthropic/CLAUDE-OPUS-4-7 should fail with Error: Unknown model: anthropic/CLAUDE-OPUS-4-7 (mirroring how anthropic/claude-fake-9000 fails today), with no provider call. Optionally include a "did you mean: anthropic/claude-opus-4-7?" hint when a case-only difference is detected.

Concrete grounded reference from the same CLI/build:

  • --model anthropic/claude-fake-9000 (model that doesn't exist in any case) is rejected upfront with Error: Unknown model: ..., exit 1, no provider call. The Unknown-model error path exists.
  • --model Anthropic/claude-opus-4-7, --model ANTHROPIC/claude-opus-4-7 (case-mismatched provider) both work — provider lookup is case-insensitive. The case-insensitive normalization exists.

The bug is the gap between these two paths: the CLI normalizes provider case but not model case, and skips the unknown-model rejection for case-mismatched model ids.

Actual behavior

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK"
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/Claude-Opus-4-7 --prompt "Just say OK"
Error: No text output returned for provider "anthropic" model "Claude-Opus-4-7".
 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK" --json
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

The error message reports the exact case the user typed — proving the model id was passed through to the provider unchanged. Note that the "No text output returned" wording is the same one #73185 patches — that issue is about empty --prompt; this one is about case-mismatched --model. Both have the same downstream symptom for the same root cause: input not normalized/validated before dispatch.

OpenClaw version

2026.4.27

Operating system

Ubuntu 24.04.4

Install method

pnpm dev

Model

anthropic/claude-opus-4-7

Provider / routing chain

openclaw -> local transport -> anthropic

Additional provider/model setup details

- Default model: anthropic/claude-opus-4-7 (set via `pnpm openclaw models set anthropic/claude-opus-4-7`).
- Anthropic auth: Claude CLI OAuth (detected by `openclaw doctor`).
- No per-agent overrides; default agent `main`; gateway `mode=local`.
- The bug reproduces under both `--local` (default in this run) and `--gateway` transports — this is upstream of transport selection, in the model-id normalization path.

Logs, screenshots, and evidence

=== Verified case-sensitivity matrix (single shell session, OpenClaw 2026.4.27 / 0450bba, Node 22.22.2) ===

EXIT  MODEL ID                                       RESULT
0     anthropic/claude-opus-4-7                      outputs: 1                                      (canonical case)
0     Anthropic/claude-opus-4-7                      outputs: 1                                      (provider uppercase OK)
0     ANTHROPIC/claude-opus-4-7                      outputs: 1                                      (provider full uppercase OK)
1     anthropic/Claude-Opus-4-7                      Error: No text output returned for provider "anthropic" model "Claude-Opus-4-7"
1     anthropic/CLAUDE-OPUS-4-7                      Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7"
1     Anthropic/CLAUDE-OPUS-4-7                      Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7"

=== The bug: case-mismatched model name reaches the provider ===

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK"
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

$ pnpm openclaw infer model run --model anthropic/CLAUDE-OPUS-4-7 --prompt "Just say OK" --json
Error: No text output returned for provider "anthropic" model "CLAUDE-OPUS-4-7".
 ELIFECYCLE  Command failed with exit code 1.

=== In-tree counter-example #1: provider name is case-insensitive ===

$ pnpm openclaw infer model run --model Anthropic/claude-opus-4-7 --prompt "Just say OK" --json | jq -r '.outputs[0].text'
OK

$ pnpm openclaw infer model run --model ANTHROPIC/claude-opus-4-7 --prompt "Just say OK" --json | jq -r '.outputs[0].text'
OK

=== In-tree counter-example #2: nonexistent model is rejected upfront ===

$ pnpm openclaw infer model run --model anthropic/claude-fake-9000 --prompt "Just say OK"
Error: Unknown model: anthropic/claude-fake-9000
 ELIFECYCLE  Command failed with exit code 1.

(Note: this rejection happens BEFORE any provider call. The Unknown-model code path exists; case-mismatched model ids should take the same path.)

=== Sanity check: the canonical lowercase model id works correctly ===

$ pnpm openclaw infer model run --model anthropic/claude-opus-4-7 --prompt "Just say OK" --json | jq -r '.outputs[0].text'
OK

Impact and severity

Affected users/systems/channels:
- Every operator who pastes a model id with mixed case (e.g. copy-paste from docs or chat where headers are title-cased) into `infer model run --model`. Linux directly observed (Ubuntu 24.04 / Node 22.22.2 / pnpm 10.33.0); platform-agnostic code path so macOS/Windows are expected to reproduce.
- New users who don't know the canonical lowercase form of model ids and try natural-feeling capitalizations (`Claude-Opus-4-7`, `CLAUDE-OPUS-4-7`).
- Users hitting this on Anthropic specifically; whether other providers exhibit the same asymmetry depends on each provider plugin's normalization (not directly verified for non-anthropic providers).

Severity:
- Annoying with confusing diagnostics. The "No text output returned" error misattributes a CLI input mistake to a provider/model fault, sending users down the wrong debugging path.
- For non-Claude providers that bill on partial requests: the case-mismatched call may incur tokens depending on whether the provider rejects pre-call or accepts and returns empty (not directly verified for non-anthropic providers).
- Inconsistent with the in-tree pattern. Provider name is case-insensitive; model name is not. Same input field, two case-sensitivity rules.
- Not a security/data-loss bug, no crash.

Frequency:
- Always, deterministic for case-mismatched anthropic model names. 100% reproduction across `Claude-Opus-4-7`, `CLAUDE-OPUS-4-7`, and `Anthropic/CLAUDE-OPUS-4-7`. Independent of model/transport.

Consequence:
- Operators waste time debugging "why doesn't claude work?" when the real issue is case mismatch in the model id.
- Inconsistency erodes operator trust in input parsing — the CLI accepts case-insensitive provider but rejects (downstream, with a misleading error) case-mismatched model.
- The "No text output returned" error string is now overloaded across at least three failure modes: empty prompt (#73185), case-mismatched model (this), and a configured provider returning empty body for valid input (closed #65394, #66506, #65076). Every overload makes the error less useful.
- No grounded evidence of missed messages, failed onboarding, or extra cost.

Additional information

- Regression status: not classified as a Regression. Last-known-good not directly observed; no bisect performed.

- Likely fix locus: the model-id normalization/lookup path (search for "Unknown model:" in `src/cli/cli-infer/` or wherever the parser lives in this build, plus the model-catalog resolution helper). Two viable approaches:
  1. Match provider behavior — make model lookup case-insensitive, normalize the matched canonical id, and dispatch the canonical id to the provider. Most user-friendly.
  2. Make model lookup case-sensitive AND route case-mismatched ids through the existing "Unknown model" rejection path. Most consistent with literal id semantics. Optionally add a "did you mean: ...?" hint.

- Suggested regression test: a unit test on the model-id resolver that asserts each of `Anthropic/claude-opus-4-7`, `anthropic/CLAUDE-OPUS-4-7`, `anthropic/Claude-Opus-4-7`, `Anthropic/CLAUDE-OPUS-4-7` either (a) resolves to the canonical `anthropic/claude-opus-4-7` and dispatches accordingly, OR (b) is rejected with `Error: Unknown model: <input>`. Pair with an existing-behavior parity test that the canonical lowercase id still works.

- Related findings:
  - #73185 (filed): empty `--prompt` reaches the provider on `--local` transport. Same misleading downstream error string ("No text output returned"), different upstream cause (empty content vs case-mismatched model). Same root-cause class: the CLI doesn't validate/normalize input before dispatching.
  - Closed issues #65394, #66506, #65076: addressed "No text output returned" in valid-input scenarios. The same string remains overloaded across at least three failure modes; this issue makes that overload one mode larger.
  - Related parsing edge case: `--model anthropic/` (provider with trailing slash, no model) currently resolves to the provider's default model and succeeds — undocumented hidden behavior. Could be filed separately if maintainers consider it a UX gap.
  - Related parsing edge case: `--model anthropic` (provider only, no slash) currently fails with `Error: Unknown model: anthropic/anthropic` — implicit `<provider>/<provider>` auto-prefix exposes internal logic in the error message. Could be filed separately.

- Dedupe checked against the openclaw issue corpus on 2026-04-28: no existing open or closed issue matches the case-sensitivity asymmetry pattern specifically. Searched "case sensitive model id", "model id case mismatch", "case insensitive provider model", "uppercase model name infer" — zero direct matches.

- Not exercised in this repro: case-sensitivity behavior for non-anthropic providers (deepseek, openrouter, etc.); behavior under `--gateway` transport (the bug is in the model-id resolver upstream of transport, but not directly verified on gateway path); aliases with case mismatches (e.g. `Opus` vs `opus`).

extent analysis

TL;DR

To fix the bug, make the model lookup case-insensitive by normalizing the input model id to lowercase before dispatching it to the provider.

Guidance

  • Identify the model-id normalization/lookup path in the codebase, likely in src/cli/cli-infer/, and modify it to normalize the input model id to lowercase.
  • Update the model catalog resolution helper to handle case-insensitive lookups.
  • Consider adding a "did you mean: ..." hint for case-mismatched model ids.
  • Write unit tests to verify the fix, including test cases for different casing scenarios.

Example

// Example of normalizing the model id to lowercase
const modelName = 'CLAUDE-OPUS-4-7';
const normalizedModelName = modelName.toLowerCase();
// Dispatch the normalized model name to the provider

Notes

  • The fix should be applied to the model-id resolver to ensure consistent behavior across different providers.
  • The existing "Unknown model" rejection path can be reused to handle case-mismatched model ids.

Recommendation

Apply the workaround by making the model lookup case-insensitive, as it is the most user-friendly approach and consistent with the provider's case-insensitive behavior.

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

One of the following — either one fixes the bug, pick whichever matches the project's case convention:

  1. Make model lookup case-insensitive too. --model anthropic/CLAUDE-OPUS-4-7 should resolve to the same model as --model anthropic/claude-opus-4-7 (the catalog already does this for the provider half) and dispatch the canonical (lowercase) id to the provider. Result: both succeed.
  2. Make model lookup case-sensitive AND normalize the catalog rejection. --model anthropic/CLAUDE-OPUS-4-7 should fail with Error: Unknown model: anthropic/CLAUDE-OPUS-4-7 (mirroring how anthropic/claude-fake-9000 fails today), with no provider call. Optionally include a "did you mean: anthropic/claude-opus-4-7?" hint when a case-only difference is detected.

Concrete grounded reference from the same CLI/build:

  • --model anthropic/claude-fake-9000 (model that doesn't exist in any case) is rejected upfront with Error: Unknown model: ..., exit 1, no provider call. The Unknown-model error path exists.
  • --model Anthropic/claude-opus-4-7, --model ANTHROPIC/claude-opus-4-7 (case-mismatched provider) both work — provider lookup is case-insensitive. The case-insensitive normalization exists.

The bug is the gap between these two paths: the CLI normalizes provider case but not model case, and skips the unknown-model rejection for case-mismatched model ids.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING