Skip to content

core: all models.dev providers disappear when the catalog fetch fails #57458

Open

Description

@shadowrootdev

Summary

When the models.dev catalog fetch fails, OpenCode ends up with an empty provider registry: every catalog provider disappears — including github-copilot, even though its credential is valid and the Copilot API answers HTTP 200 in 0.65s. Only the providers advertised by the OpenCode Console config (opencode-go) survive, because they are registered through a different code path.

GitHub Copilot has a second, independent gate: its plugin registers the provider only if the model sync succeeded, and that sync has a hard-coded 5s timeout. One timeout removes the provider, and nothing ever retries it.

Environment

  • opencode version: 2.1.1
  • OS: Ubuntu, Linux x64
  • Terminal: xterm-256color, COLORTERM=truecolor
  • Shell: /bin/bash
  • Install/channel: latest
  • Active plugins: none provider-related (local guardrail plugins and MCP servers only; none of them touch the provider layer)

Reproduction

Deterministic repro that does not involve GitHub Copilot at all:

  1. Start a server with the network to models.opencode.ai blocked:
    HTTPS_PROXY=http://127.0.0.1:9 HTTP_PROXY=http://127.0.0.1:9 opencode serve --port 46003
    
  2. opencode api --server http://127.0.0.1:46003 provider.list → {"data":[]} — zero providers, even though the local catalog cache holds 231 providers.
  3. Unblock the network, restart, run the same command → the providers are back.

On the normal (networked) install:

  1. ANTHROPIC_API_KEY=dummy opencode models | grep -c '^anthropic/' → 0
  2. GITHUB_TOKEN=dummy opencode models | grep -c '^github-copilot/' → 0
  3. opencode models | grep -c '^opencode-go/' → 29 — only the console-provided provider survives.

Copilot-specific path:

  1. opencode auth login github-copilot (device code) → credential stored, active: true
  2. curl -sS -m 10 -H "Authorization: Bearer $TOKEN" https://api.individual.githubcopilot.com/models → HTTP 200 in 0.65s
  3. opencode models | grep -c '^github-copilot/' → 0
  4. server log: WARN ... "failed to sync GitHub Copilot models" cause="TimeoutError: The operation timed out."

Expected Behavior

  • A failed models.dev fetch should not empty the provider registry: the cached catalog should be used, or the previously loaded registry should be kept.
  • A provider whose credential is valid should stay registered even when the model sync fails, and the sync should be retried.

Actual Behavior

  • The provider registry is populated from a live fetch of https://models.opencode.ai/api.json. The cache written by the same service (models-dev:catalog in the kv table, 231 providers, freshly written) did not seed the registry, so a failed fetch leaves zero providers.
  • The GitHub Copilot plugin only registers the provider on sync success:
    if (l.models) { h.add({ info: S.provider, models: ..., sourceConnection: l.connection }); return }
    for (let F of S.models.keys()) h.models.update(...)   // else: rewrites packages, never adds the provider
    and it also bails out earlier with if (!S) return when github-copilot is not already in the registry.
  • The sync uses AbortSignal.timeout(5000). The endpoint answers in 0.65s via curl, but the log shows the timeout.
  • The plugin re-syncs only on integration.Switched (new login / credential switch) or server start. opencode api POST /api/location/reload does not re-trigger it, so the state is sticky.
  • Provider availability is non-deterministic: a freshly started server first listed github-copilot, anthropic, opencode, and a few minutes later the same server listed only opencode-go.

Additional Context

Activity

  1. opencode-agent commented on Oct 20, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I tried to reproduce this on 2.1.1 (Linux, clean profile, no plugins) but couldn't get providers to disappear for good:

    • models.opencode.ai blocked (proxy pointed at a closed port, or at one that accepts connections and never answers, which gives the same Operation timed out after '10s' as your log): provider.list was empty only for the first few seconds after opencode serve started, while the first catalog fetch was pending. After the fetch failed it listed anthropic and opencode from the built-in catalog. They were still listed about 6 minutes later, after the 5-minute refresh had failed again. With the models-dev:catalog cache already written, providers showed up after about 4s.
    • Networked, clean profile: ANTHROPIC_API_KEY=dummy opencode models listed 20 anthropic/ models, not 0.
    • GitHub Copilot with a model sync that times out (credential pointed at an endpoint that never answers): the log shows failed to sync GitHub Copilot models ... TimeoutError, but opencode models still listed 38 github-copilot/ models. When the sync fails, the plugin falls back to the catalog's Copilot models instead of removing the provider.

    So the only part I could confirm is a short empty provider list right after startup. That looks like the startup race discussed in #41360. Since you have OpenCode Console / Go credentials stored, the providers going missing for good (and opencode models showing only opencode-go) fits better with #52363 / #55082.

    It would help to know:

    • Does provider.list stay empty if you wait 30–60s after starting the blocked server, or only right after start?
    • Do the providers come back with a fresh profile that has no opencode / opencode-go credentials stored (e.g. temporary XDG_DATA_HOME/XDG_CONFIG_HOME)?

    Related issues:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions