anthropics / anthropics/claude-code

Workflow agent() model silently replaced by CLAUDE_CODE_SUBAGENT_MODEL when the id is not in the gateway-discovered model list

オープン
#89,972 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:agents area:providers bug platform:macos
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

# Workflow agent() model silently replaced by CLAUDE_CODE_SUBAGENT_MODEL when the id is not in the gateway-discovered model list

## Environment

- Claude Code 2.1.245 (also observed on 2.1.226), macOS (darwin arm64)
- Custom gateway: `ANTHROPIC_BASE_URL` pointed at a self-hosted Anthropic-wire proxy, `ANTHROPIC_AUTH_TOKEN` set, `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1`
- `CLAUDE_CODE_SUBAGENT_MODEL` set to a gateway-advertised id (`claude-delegate-capable`)

## What happens

With gateway model discovery enabled, Claude Code fetches `GET /v1/models` from the gateway and treats the returned list as the exhaustive vocabulary for **subagent** model resolution (Workflow `agent()` `model:` opts). Any requested model id absent from that list is **silently replaced with `CLAUDE_CODE_SUBAGENT_MODEL`** before the request leaves the machine:

- No error, no warning, no UI indication at call time.
- The subagent's `agent-*.meta.json` records only the **requested** id (`requestedModel`), which actively misleads post-hoc inspection — the transcript's served model is the only local trace, and nothing correlates the two.
- The substituted request reaches the wire as the fallback model. Verified at the gateway with a live SSE tap of per-request events: agents whose `agent()` opts requested `claude-fable-5`, `claude-fable-5[1m]`, and (in a later repro) a class id added to the gateway after session start all arrived as `model_requested=claude-delegate-capable`.

The main-loop model is unaffected (passes through verbatim); only subagent resolution is gated this way.

## Reproduction

1. Point Claude Code at any Anthropic-compatible gateway whose `/v1/models` returns a *partial* model list (e.g. it omits `claude-opus-5` while happily routing it on `/v1/messages`). Set `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1` and a `CLAUDE_CODE_SUBAGENT_MODEL` that *is* advertised.
2. Run a Workflow with `agent('say hi', {model: 'claude-opus-5'})` (any valid id not in the discovery list).
3. Observe on the gateway that the request arrives with the `CLAUDE_CODE_SUBAGENT_MODEL` value instead; observe locally that no error or warning was emitted and the agent meta records the requested id.

A second wrinkle makes this sharper: the discovery list appears to be fetched **once per session**. A model added to the gateway's `/v1/models` after session start keeps being silently substituted for the rest of that session, so even a "fixed" gateway doesn't stop the behavior for running sessions.

## Impact

A multi-day silent failure in our case: an adversarial-review stage explicitly specified as "strongest model" ran on a different (cheaper) model class for three days across three sessions. Nothing surfaced it; diagnosis required tapping the gateway's event stream and hand-correlating subagent transcripts. The failure mode is the worst kind for model-quality work — outputs remain plausible, so nothing looks wrong.

## Expected behavior

Gating subagent models on the discovered list is a defensible safety choice (an unknown id might 404). The defect is the **silence**. Any of these would be fine:

1. **Refuse**: fail the `agent()` call with a clear error naming the unadvertised id, or
2. **Honor**: pass the id through and let the gateway decide (it may route ids it doesn't advertise), or
3. **Substitute loudly**: keep the fallback but emit a visible warning and record both requested *and* resolved ids in the subagent metadata.

Silent substitution converts a configuration gap into invisible model downgrades.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start at the gateway model-discovery flow and Workflow agent() subagent model-resolution entry points, then reproduce with a partial GET /v1/models response and an unlisted model id. Trace where the requested id becomes the wire model and how agent-*.meta.json is written. Done means the chosen handling is visible and the requested and resolved models are no longer misleadingly represented.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
ai, api
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
45/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。