Skip to content

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

Closed as not planned

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:

  2. shadowrootdev commented on Oct 21, 2026

    @shadowrootdev
    Author

    Thanks for the detailed triage — you were right, and my report overstated the cause.

    Answering your two questions (same install: OpenCode 2.1.1, Ubuntu, default data dir):

    1. Is provider.list empty only right after start, or does it stay empty?
    Both, depending on the run. With a clean profile and the network to models.opencode.ai blocked, the list was empty at t+10s, then listed opencode (from the built-in catalog) from t+30s on, and was still there at t+90s. On the real data dir, four servers started simultaneously with an identical config all reported 0 anthropic/ models, while a server started a few minutes later with the same config reported 20. So there is no stable state: the outcome depends on whether the catalog fetch succeeded at that moment.

    That flakiness matches this host — Failed to fetch models.dev is logged here 44 times since 2026-09-30, with getaddrinfo ETIMEOUT models.opencode.ai and TimeoutError: Operation timed out after '10s', retried every 5 minutes.

    2. Do the providers come back with a fresh profile that has no opencode / opencode-go credentials?
    Yes. With an empty XDG_DATA_HOME/XDG_CONFIG_HOME, the built-in catalog materializes and ANTHROPIC_API_KEY=dummy opencode models lists 20 anthropic/ models, with or without the network blocked.

    Where I was wrong: I reported "a failed models.dev fetch leaves zero providers" as a stable state. It was a startup race, and provider.list measured inside that window returns 0 while the CLI already lists models — I was treating provider.list as the source of truth for opencode models, which it is not. My claim that the models-dev:catalog kv cache does not protect the bootstrap is not something I can support either: the cache is present and populated in every case I measured.

    What still matches: the persistent case in this report — github-copilot absent from the registry while opencode-go keeps working, with a valid credential and an API answering 200 — is what #52363 and #55082 describe, and I agree it belongs there.

    Two points that may help whoever picks those up:

    • The GitHub Copilot plugin can only register github-copilot if it is already in the registry (if (!S) return), so it is gated by the catalog load as well as by its own sync. Should it be able to register itself independently of the catalog?
    • POST /api/location/reload did not re-trigger the models.dev fetch in my tests; only the 5-minute refresh timer did.

    I will not duplicate #52363 / #55082 — if this issue should be closed in their favour, that is fine.

  3. shadowrootdev commented on Oct 21, 2026

    @shadowrootdev
    Author

    Two further data points from the same install, which may be the missing piece.

    The catalog fetch is fighting a broken IPv6 path. models.opencode.ai publishes an AAAA record, but the host has no global IPv6 route (only link-local fe80::/64 on the VM bridge interfaces):

    $ getent ahostsv6 models.opencode.ai
    2606:4700:10::6814:2011  STREAM models.opencode.ai
    $ ping6 -c1 -W3 2606:4700:10::6814:2011
    connect: Network is unreachable
    $ curl -6 -sS --max-time 10 https://models.opencode.ai/api.json
    curl: (7) Failed to connect to models.opencode.ai port 443 after 64 ms: Could not connect to server
    $ curl -4 -sS --max-time 30 https://models.opencode.ai/api.json
    http=200  size=5969390  total=2.77s / 4.19s / 4.35s   (3 runs)

    The A record resolves and the IPv4 path works, so the problem is specifically the unreachable AAAA address. Bun's getaddrinfo/fetch can pick that address, which fits the getaddrinfo ETIMEOUT models.opencode.ai seen in the log.

    The payload barely fits the budget. The catalog is 5.9 MB and takes 2.8–4.3 s over IPv4 on a good run, while the fetch is capped at Ms("10 seconds") (TimeoutError: Operation timed out after '10s'). With a 5-minute refresh loop over a 6 MB body, a slow link puts the fetch right at the edge of that cap.

    The outcome is a coin flip, not a state. Same data dir, same config, same environment: four servers started simultaneously all reported 0 anthropic/ models from opencode models; a server started three minutes later with the same configuration reported 20. The models-dev:catalog kv cache is present and complete (232 providers, github-copilot included) in both cases — so the cache is not what decides the outcome.

    That last point looks like the actual defect: materialization of the catalog providers is not tied to the fetch succeeding. opencode-go survives because it is registered from the Console config (GET /api/v2/config) through a different path.

    One more inconsistency worth flagging: during the startup window GET /api/provider can return an empty list while opencode models already lists models, and the two can disagree for the very same server. I was misled by that when filing this report, and it would mislead anyone else trying to reproduce it.

    Questions that would help:

    • Is the 10 s cap on a ~6 MB payload intentional, or is it inherited from a smaller catalog?
    • When the live fetch fails, should the provider list fall back to the bundled snapshot instead of leaving the registry empty?
    • Is there a supported way to force a catalog reload? POST /api/location/reload did not re-trigger the fetch in my tests; only the 5-minute timer did.
  4. opencode-agent commented on Oct 23, 2026

    @opencode-agent
    Contributor

    Closing this since there's been no reply in the past few days. On 2.1.1 I couldn't reproduce providers disappearing for good when models.opencode.ai is unreachable. With models.dev blocked, the provider list was empty only for the first few seconds after startup. Once the fetch failed it filled from the built-in catalog, and it stayed filled after the 5-minute refresh failed too. A Copilot model sync that timed out also left the github-copilot models listed.

    Since you have OpenCode Console / Go credentials stored, what you're seeing most likely matches one of these open issues. Please follow them for updates:

    If providers still stay empty 30–60s after starting the server with a fresh profile that has no opencode / opencode-go credentials, please reply here with the steps and we can reopen this.

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