Desktop remote-SSH: native model picker sends bare model id, hangs/falls back instead of resolving to org's custom-role slug

Status Open
Reported on v2.1.266
Maintainer reply None cached
Activity 0 comments · opened Sep 14, 2026

Environment

  • Claude Desktop (macOS) connected via remote-SSH to a Linux/arm64 devcontainer
  • Remote CLI (ccd-cli, the binary Desktop deploys for SSH sessions): reproduced on 2.1.266, 2.1.268, and 2.1.270 — not version-specific
  • Org has a custom model-role configuration (orgModelDefaultCache.data_source: "custom_role_configuration"), exposing models under @anthropic/<model-id>-style slugs (e.g. @anthropic/claude-opus-5)
  • Requests route through an internal API gateway (custom ANTHROPIC_BASE_URL + auth headers) — confirmed not relevant to the bug (see below)

Symptom
Picking "Opus 5" (or Sonnet 5, or Haiku 4.5) from Desktop's native /model picker over a remote-SSH connection shows:

Claude Code notice: Model "claude-opus-5" is restricted by your organization's settings. Using claude-sonnet-5 instead.

...and the session silently runs on the org default model instead of the one selected.

Repro (isolated from Desktop, direct CLI invocation over SSH)

$ ccd-cli -p "say hi" --model claude-opus-5 --output-format json
→ hangs indefinitely (30s+ timeout, no output, no error)

$ ccd-cli -p "say hi" --model "@anthropic/claude-opus-5" --output-format json
→ returns in ~4s, modelUsage: {"@anthropic/claude-opus-5": {...}}

$ ccd-cli -p "say hi" --model opus --output-format json
→ returns in ~4s, resolves internally to "@anthropic/claude-opus-5"

Same pattern reproduces identically for claude-sonnet-5/claude-haiku-4-5-20251001 vs. their @anthropic/-prefixed and alias forms — not model-specific.

What's been ruled out (each tested directly, no effect):

  • CLI version skew (reproduced identically on 2.1.266, 2.1.268, 2.1.270)
  • ANTHROPIC_DEFAULT_OPUS_MODEL/_SONNET_MODEL/_HAIKU_MODEL env vars — these only affect alias resolution (opus→…), not literal bare-id requests
  • availableModels allowlist in managed settings — added the bare ids explicitly, zero change in behavior
  • Server-side entitlement data is not the issue — modelAccessCache already lists the bare id as entitled: true
  • Not a network/gateway issue — packet capture of the actual outgoing request shows the model field already silently rewritten to the org default ("model":"claude-sonnet-5") before the request is built, or (more often) no request is ever sent at all (process hangs pre-network)

Root cause (inferred from behavior, not source access)
There appear to be two separate resolution paths for a --model value:

  1. Fast path for known aliases (opus/sonnet/haiku) and exact @anthropic/-prefixed custom-role slugs — resolves directly from the already-cached modelAccessCache/orgModelDefaultCache.
  2. A different path for a bare canonical id that isn't a recognized alias or exact custom-role slug — even though the same cache already marks it entitled: true. This path appears to trigger a live re-verification (matches idle gateway connections observed sitting in epoll_wait), which hangs or intermittently times out and falls back to the org default.

Suggested fix

  1. In the CLI's model resolution: when an org has custom_role_configuration active, canonicalize a bare model id to its corresponding @anthropic/<id> slug using data already present in modelAccessCache/orgModelDefaultCache, rather than requiring an exact string match or triggering additional verification.
  2. Bound whatever call the slow path makes with a timeout + clean fallback (matching what Desktop's UI eventually shows) — it should never hang indefinitely in -p/scripted usage.
  3. Separately, in Desktop: the native model picker should send the identifier form already known to work (the alias, or the exact @anthropic/-prefixed slug it displays in its own picker UI) rather than constructing the bare canonical id when handing off to a remote-SSH session.

🤖 Generated with Claude Code

View original on GitHub ↗