[BUG] modelPicker skips an `opusplan` row as already covered, but the picker shows no Opus Plan Mode row
Summary
A modelPicker row whose model is opusplan never reaches the /model picker in append mode. Claude Code treats the row as covered by the built-in lineup. The built-in lineup carries no Opus Plan Mode row in an ordinary session, so nothing covers it.
opusplan is a mode, not a model. --model opusplan runs Opus during plan mode and Sonnet during execution. --model opus runs Opus throughout. The coverage check compares resolved models, so it treats the two as the same row.
Relationship to #26556
#26556 requests Opus Plan as a discoverable /model option. This issue is not a duplicate of it, and not a second request for the same feature.
modelPicker shipped in v2.1.242 as the documented way to add rows to the /model picker. It is the mechanism a user would reach for to satisfy #26556 themselves. It works for every other value tested. It does not work for opusplan, and it gives no indication why.
Environment
| | |
| --- | --- |
| Claude Code | 2.1.246, also reproduced on 2.1.243 and 2.1.245 |
| Platform | macOS 15.6, native install |
| Auth | Claude Max subscription, first-party API |
| Session model | opus[1m] |
| Built-in lineup | Default, Opus (1M context), Fable, Sonnet, Haiku |
Steps to reproduce
- Save this to
/tmp/mp.json:
{
"modelPicker": {
"options": [
{
"model": "opusplan",
"label": "Opus Plan Mode",
"description": "Use Opus in plan mode, Sonnet otherwise"
}
]
}
}
- Run
claude --settings /tmp/mp.json(absolute path required). - Run
/model.
Expected
The picker shows the built-in lineup, then an "Opus Plan Mode" row.
The settings reference states the contract for the model field:
Each row'smodelis taken verbatim, so it accepts anything--modelaccepts: an alias such asopus, an Anthropic model ID, or a provider-format ID for Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, or an LLM gateway.
opusplan is such an alias, and Claude Code advertises it in its own help text:
$ claude -p "/model"
Usage: /model <name>. Available: sonnet, opus, haiku, fable, best,
sonnet[1m], opus[1m], fable[1m], opusplan, default, or a full model ID.
Actual
The picker shows the built-in lineup and nothing else. Five rows, no Opus Plan Mode row, and no message.
Why this is not the documented skip
The same page documents a skip:
With it off, Claude Code skips a listed model that the built-in lineup already covers.
That sentence correctly explains several other rows (see the table below). It does not explain this one, because the built-in lineup carries no Opus Plan Mode row in this session.
Claude Code does ship such a row, and offers it on one condition only: the session model is already opusplan. A session started with {"model": "opusplan"} boots as "Opus Plan" and its picker gains a sixth row, "Opus Plan Mode ✔".
So the row is reachable only by users who already run the mode it selects. modelPicker is the documented way out of that loop, and it is the one route that does not work.
Evidence
Each row below ran alone and in combination, in both merge modes, via --settings, on 2.1.246.
| model | Append | Replace | Reading |
| --- | --- | --- | --- |
| opusplan | dropped | shown | the defect |
| opus | dropped | shown | expected, lineup covers Opus |
| best | dropped | shown | expected, lineup covers it |
| claude-haiku-4-5-20251001 | dropped | shown | expected, lineup covers Haiku |
| claude-sonnet-4-5-20250929 | shown | shown | expected, outside the lineup |
| claude-opus-4-1-20250805 | dropped | dropped | expected, retired |
| claude-3-5-haiku-20241022 | dropped | dropped | expected, retired |
Replace mode renders the opusplan row, which proves the row parses, validates, and is servable. Only the coverage check rejects it:
1. Default (recommended)
2. Opus Plan Mode Use Opus in plan mode, Sonnet otherwise
3. Opus (1M context) ✔ Opus 5 with 1M context
Impact
There is no way to keep the maintained lineup and add Opus Plan Mode. The three available workarounds are each worse than the documented behavior:
replaceBuiltInOptions: trueand hand-maintain the whole lineup, so future model additions stop appearing."model": "opusplan", which changes the default for every session.- Do without the row.
Requested change
Treat opusplan as distinct from opus in the coverage check. A mode and a model are not the same row, and a user who names the mode explicitly has asked for something the lineup does not offer.
Secondary request (independent)
Say something when a row is skipped for coverage. The documentation describes two visible outcomes, a dropped row and a grayed-out row that "shows the reason". A row skipped for coverage produces neither, so a user cannot distinguish it from a typo in model or a settings file that failed to load.
Claude Code already emits a warning of the right shape for the exact-duplicate case:
model options: dropping duplicate row "<label>" (value <model>)
One line in that shape, for the coverage skip, would make the feature self-diagnosing.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗