Repository navigation
Description
Summary
In the TUI model picker (/models, or Ctrl+X then M), provider grouping and provider names are only shown when at least one integration has a saved connection. Providers declared in opencode.json (providers.<id>), which is the documented way to configure a custom gateway, never create a connection, so anyone using that path gets a flat, ungrouped, unlabeled model list. Combined with "free models first" sorting, this makes the picker look like it does not contain the configured provider at all.
Environment
- opencode version: 2.0.28 (installed CLI; background service still reported 2.0.27 while reproducing)
- OS: Windows_NT 10.0.26300 (win32 x64)
- Terminal: Windows Terminal (
WT_SESSIONset),TERM=xterm-256color,TERM_PROGRAMunset - Shell:
C:\WINDOWS\system32\cmd.exe; the agent shell used for the commands below is PowerShell 7.6.6 - Install/channel:
latest, installed via bun global (~/.bun/install/global/node_modules/@opencode/cli/bin/opencode.exe) - Active plugins:
git-ai(local,~/.config/opencode/plugins/git-ai.ts),utf8-encoding(local,~/.config/opencode/plugins/utf8-encoding.ts)
Reproduction
-
Start from a config with no saved integration connections (
opencode auth listprintsNo authenticated integrations,opencode api get /api/credentialreturns{"data":[]}). -
Declare a custom provider using the documented recipe (Providers -> Custom / Gateways, Models -> Compatible) instead of
/connect:{ "model": "acme/coder", "providers": { "acme": { "name": "Acme Gateway", "package": "@opencode/ai/providers/openai-compatible", "settings": { "baseURL": "https://gateway.example.com/v1", "apiKey": "sk-***" }, "models": { "coder": { "name": "Coder" } } } } }Migrating a V1 config instead (
provider.acme.npm+provider.acme.options->providers.acme.package+providers.acme.settings) has the same effect. -
Reload, then confirm the models resolve server-side:
opencode reload opencode models # lists acme/coder opencode api get /api/model # includes providerID "acme"
-
Open the model picker (type
/models, or press Ctrl+X then M). -
Observe the list.
Expected Behavior
Because the provider is enabled and its models resolve (steps 2-3), the picker should present those models the same way it presents any other provider: grouped by provider, with the provider name visible (and Favorites / Recent sections available).
Actual Behavior
The picker renders one flat list with:
- no provider grouping, and no provider name anywhere in the rows,
- no Favorites and no Recent sections,
- ordering that puts free models first and then newest-first, so built-in free models occupy the top of the list.
There is no error. The configured provider's own models are present, but they are unlabeled and pushed below the fold, so the picker appears to contain "none of my models" and "only unfamiliar models".
Connecting any single catalog provider makes grouping appear globally, which confirms the gate below. Repro is consistent, not intermittent.
Additional Context
Cause
The model dialog's list builder computes one global boolean from the integration list and uses it to gate grouping, provider names, and the Favorites/Recent sections:
// 2.0.27: ej(t) 2.0.28: aj(t)
function aj(t) { return t.some((r) => r.connections.length > 0) }and in the model dialog itself:
category: w() ? provider.name ?? provider.id : undefined // grouping + label
description: w() ? provider.name ?? provider.id : undefined // provider name
M = w() ? favorite : [] // Favorites section
q = w() && filter === "" // Recent section
// and when a query is typed, grouping is skipped entirely:
if (j) return ...fuzzysort(...) // keys: ["title", "category"]So "does the user have any provider" is implemented as "has the user ever connected an integration". Providers that come from configuration are explicitly present in the integration list, but with an empty connections array, so they do not satisfy the gate. Example from /api/integration on this machine (custom provider declared in opencode.json):
{
"id": "acme",
"name": "Acme Gateway",
"methods": [{ "type": "key" }],
"connections": []
}Why this affects documented usage
The docs route custom gateways through configuration, not /connect:
- Providers -> Custom: define
providers.<id>withpackage,settings,models. - Providers -> Gateways: "For a gateway that exposes an OpenAI-compatible API but has its own model inventory, define a custom provider instead."
- Models -> Compatible: same recipe for any OpenAI-compatible server.
None of those mention /connect, and providers.<id> produces no connection. /connect (and auth) manage integration accounts against catalog providers. A provider that is not in models.dev cannot be connected at all, so a self-hosted or internal gateway can never satisfy the gate.
Note /api/integration is not equivalent to models.dev: it is models.dev entries + built-in websearch integrations + any provider declared in config.
Workaround
Connect any provider that exists in the catalog, which flips the boolean globally:
opencode service set env DEEPSEEK_API_KEY sk-***
# or: /connect in the TUIThis is only a workaround; it requires storing an unrelated credential purely to change picker rendering.
Related issue
A second, independent defect in the same dialog: a model configured with "name": "" renders as a blank row and is unsearchable, because the display-name fallback uses ?? and therefore keeps the empty string instead of falling back to the model ID. Reported separately.
Activity
Ivony commentedon Oct 14, 2026
Related: #55289 (independent defect in the same dialog - empty configured
namerenders blank and is unsearchable).opencode-agent commentedon Oct 14, 2026
Thanks for the detailed write-up and root-cause analysis. I think this is a duplicate of #49309, which reports the same thing: the model picker drops provider names and grouping for providers declared in config, because the check only looks for saved integration connections.
Possible duplicates and related items, ordered by likelihood:
useConnected()/connections.length > 0gate./modelshides provider names forproviders.<id>declared inopencode.jsoncwith inlinesettings.apiKey. Same symptom./modelswithout a connected integration. Same gate, different symptom."name": "". Not covered by this duplicate.Your notes on the Favorites/Recent sections, the search path and the free-first ordering would be useful on #49309. The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.
Ivony commentedon Oct 14, 2026
Agreed, this is a duplicate of #49309 — same root cause (provider names/grouping gated on saved integration connections rather than configured providers), and #49309 plus #50322 cover it more thoroughly.
Closing this one proactively rather than waiting for the automatic duplicate close.
Two notes carried over so nothing is lost:
"name": ""renders a blank row and is unsearchable) is tracked in models: empty configured name renders blank and is unsearchable (V1 -> V2 regression: truthy check became ??) #55289, which the bot reproduced.Thanks for the triage.
opencode-agent commentedon Oct 14, 2026
Thanks for confirming, and for moving the configured-vs-connected point over to #49309. That keeps everything in one place. #55289 still covers the blank-row problem separately.
opencode-agent commentedon Oct 15, 2026
Closing this as a duplicate of #49309, as agreed above. Please follow #49309 (and #50322) for the provider grouping and names in the model picker, and #55289 for the blank-row problem with
"name": "". Thanks again for the detailed report.