Skip to content

Remote model picker X's out all slash-prefixed models as "OpenRouter auto" and blocks selection, even when a configured OpenAI-compatible profile serves them #840

Description

@D0nnieD4rk0

Type: Bug
Version: v0.68.0
Severity: High (models become unselectable in /model for hours at a time)

Summary

In remote sessions, models whose ids contain / (e.g. vendouple/gpt-5.6-sol, MarcosFRG/deepseek-v4-pro, gggff123/glm-5.2) get classified as OpenRouter "auto" routes and rendered with a × (unavailable) in the picker, which hard-blocks selection. This happens even when a configured OpenAI-compatible profile (pollinations, opencode zen, etc.) has the exact model id in its live catalog and the server can serve it fine.

Repro context

  • jcode running in remote mode (remote=true on every model_picker_open event in the log).
  • Providers are configured as OpenAI-compatible profiles (pollinations, opencode_zen, nvidia_nim, ...) with live model catalogs.
  • No OPENROUTER_API_KEY is set locally (the models come from the configured profiles, not OpenRouter).

Observed behavior (from ~/.jcode/logs/jcode-2026-08-07.log)

Same session, two picker states:

State model_picker_open event Available
Full catalog (detailed routes attached) entries=751 entries_available=532 (13:00–14:53) Mostly selectable
Names-only catalog (no route frame) entries=1225 entries_available=243, by_provider=auto:804 (12:43) 804 slash-model rows X'd
Names-only catalog (no route frame) entries=1528 entries_available=275, by_provider=auto:1074 (23:30) 1074 slash-model rows X'd

The pattern "models get X'd after a few hours": the session runs on the good full catalog, then a names-only catalog update/refresh race drops the detailed route frame, the picker falls back to remote_model_routes_fallback, and every slash-prefixed model becomes unselectable.

Root cause

jcode-base/src/provider/catalog_routes.rs, remote_model_routes_fallback (lines 841–1053):

  1. Any model id containing / is assumed to be an OpenRouter model (lines 892–920): it's routed to build_openrouter_auto_route(model, auth.openrouter != AuthState::NotConfigured, ...), so with no local OPENROUTER_API_KEY the route is available: false with no detail.
  2. That branch continues before remote_openai_compatible_route_for_model(model) (line 1006) is ever consulted, so a configured profile whose live catalog contains the exact id is never given a chance to produce an available route.
  3. remote_current_openai_compatible_route_for_model (lines 1102–1127) also bails on /-containing ids (if model.contains('/') { return None }), so the "current provider" fallback can't help either.
  4. The TUI then hard-blocks selection on any !route.available row: jcode-tui/src/tui/app/inline_interactive.rs lines 3348–3359 (PickerAction::Modelunavailable_model_route_message → "Model unavailable"), with the × rendered by picker_row_marker in jcode-tui/src/tui/ui_inline_interactive.rs lines 115–125.

The client mislabels working models as unconfigured OpenRouter purely because of the / heuristic; the server-side catalog (pollinations, opencode zen) contains them and serves them fine.

Suggested fix

In remote_model_routes_fallback, before the model.contains('/')build_openrouter_auto_route branch, consult remote_openai_compatible_route_for_model(model) first:

  • If a configured profile's live cache (or static model list) contains the exact id → emit that profile's route with available: true and continue.
  • Only fall back to the OpenRouter "auto" route when no profile matches (and mark unavailable only then, if OpenRouter is genuinely unconfigured).

Also relax remote_current_openai_compatible_route_for_model so slash-ids are matched when the current provider profile's catalog actually contains them (instead of blanket-skipping all / ids).

Optionally: consider not hard-blocking selection on !available rows — show the reason but still let the user attempt the switch when a route exists (the server is the real authority on whether a model is servable).

Workarounds (no rebuild needed)

  • Type /model <name> instead of using the picker — the typed path (backend.rs:692, Request::SetModel) sends the bare id to the server with no availability gate.
  • Set a dummy OPENROUTER_API_KEY locally — the availability check is presence-only, so the picker rows flip to selectable; the dummy key is never used since selection still sends the bare model id.

Metadata

Metadata

Assignees

No one assigned

    Labels

    autonomous: likelyProbably hands-off: clearly worth fixing, agent can do it, minor judgment needed.bugSomething isn't workingtriage: fixed-pending-releaseFixed in code/committed; will close automatically on next releasetriage: reproducibleClear repro + clear fix path

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions