Skip to content

v2: config-declared custom-provider models silently become 0 in catalog when models.opencode.ai fetch fails at boot (provider survives, resolution-by-ID still works) #41360

Open

Description

@Meligy

Description

Custom providers declared entirely in config (provider. with an inline models map, using model IDs not in the public models.opencode.ai catalog) end up with 0 models and disappear from the picker — with no error shown. The provider entry survives; only its models are dropped.

Cause: the boot fetch of https://models.opencode.ai/api.json is swallowed on failure (logError "Failed to fetch models.dev" then Effect.ignore), and custom-model hydration then produces an empty models map instead of keeping the config-declared models. The empty catalog persists until the server rebuilds during a successful fetch.

Model resolution by explicit ID keeps working the whole time (run -m / succeeds), so only the catalog/listing path breaks, not resolution. Typing the model name into the picker search box returns nothing, confirming the catalog is genuinely empty rather than filtered.

Related but not duplicate: #35090, #35713, #32986, #34730 describe loud errors/crashes from the models.dev fetch; this is silent, persistent loss of config-declared custom models while the app looks healthy.

Expected: config-declared models should register and list regardless of the models.opencode.ai fetch; a failed fetch should degrade gracefully (warn + keep declared models), not zero them out.

Plugins

none required to reproduce (also repro'd with plugins disabled)

OpenCode version

Desktop beta 0.0.0-beta-202608090143; CLI opencode2 0.0.0-next-15820

Steps to reproduce

  1. Add a custom provider in opencode.json with model IDs not in models.opencode.ai:
    "provider": { "myproxy": { "npm": "@ai-sdk/openai-compatible", "name": "My Proxy",
    "options": { "baseURL": "{env:MY_BASE_URL}", "apiKey": "{env:MY_TOKEN}" },
    "models": { "some/private-model-id": { "name": "Private Model", "tool_call": true,
    "limit": { "context": 200000, "output": 64000 } } } } }
  2. Start while https://models.opencode.ai/api.json is unreachable/slow (block or throttle it).
  3. Open the picker (or GET /api/model).

Result: picker empty; model name absent even when typed in search; GET /api/model -> {"data":[]}; GET /api/provider shows the provider with 0 models; no UI error; persists across relaunches until the catalog rebuilds during a good fetch.
Verified: same config + reachable fetch + fresh server start -> full catalog lists all declared models.

Screenshot and/or share link

No response

Operating System

macOS 26.6.1 (arm64)

Terminal

OpenCode Beta desktop app (also repro'd via opencode2 CLI)

Activity

  1. benjaminfrombe commented on Sep 17, 2026

    @benjaminfrombe

    I think a good part of what is reported here is a startup race rather than a config parsing problem: serve starts answering requests before the catalog is hydrated, so what you get back depends entirely on how soon after startup you ask.

    Repro on 2.0.6 (macOS arm64), with six providers declared in ~/.config/opencode/opencode.jsonc:

    OPENCODE_SERVER_PASSWORD=test opencode serve --port 19060 --hostname 127.0.0.1 &
    START=$(date +%s.%N)
    for i in $(seq 1 8); do
      T=$(echo "$(date +%s.%N) - $START" | bc | cut -c1-4)
      P=$(curl -s -u opencode:test localhost:19060/api/provider | jq '.data | length')
      M=$(curl -s -u opencode:test localhost:19060/api/model    | jq '.data | length')
      echo "t+${T}s providers=$P models=$M"
      sleep 1
    done
    t+0.03s  (connection refused)
    t+1.25s  providers=0  models=70
    t+2.53s  providers=6  models=138
    t+3.64s  providers=6  models=138
       ... stable from here on
    

    Two things stand out:

    1. The window is real but short. Before roughly 2.5s the catalog is empty or partial; after that it is complete and stays that way. Nothing about the config changes in between, so a client that asks once at the wrong moment concludes the providers do not exist.

    2. The endpoints disagree during that window. At t+1.25s /api/provider returns zero entries while /api/model already returns 70. Anything that guards on "no providers, so bail out" gives up even though the model list is populated.

    There is also a staged fill: the built-in provider lands about a second before the ones declared in config, so an early read can produce a catalog that looks valid but silently omits every user-declared provider. That is the failure mode I would worry about most, because it does not raise an error anywhere.

    The CLI is not affected, which I think is why this looks like an API-only bug: opencode models resolves through Provider.Service.list() in-process and waits for hydration, while the HTTP handler reads Catalog.Service state as it stands at request time.

    For anyone hitting this from a client: the server listening on ... line and /api/info both become available before the catalog does, so neither is usable as a readiness signal today. Polling until the contents stop changing works, but an explicit signal (or having the list endpoints await hydration) would obviously be nicer.

    Happy to test a patch against this setup if that helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions