Skip to content

acp: session/new catalog ignores config providers, agents, and default model since 2.0.4 #50236

Closed

Description

@cpu150

Description

Since v2.0.4, opencode acp stops loading user configuration when building the session/new catalog (the configOptions list). Custom providers (e.g. Ollama), custom agents/modes, and the configured default model are missing; ACP clients like Zed only get built-in models (opencode/*, Google) and the default build/plan modes.

  • Works on 2.0.3 (global and project config both honored).
  • Broken identically on 2.0.4, 2.0.6, 2.0.11.

Plugins

None. Config only: ~/.config/opencode/opencode.json with provider.ollama (baseURL http://localhost:11434/v1), a non-default model, and custom primary agents (~/.config/opencode/agents/*.md). Ollama daemon confirmed running at localhost:11434.

OpenCode version

2.0.4 / 2.0.6 / 2.0.11 (regression); 2.0.3 working

Steps to reproduce

  1. Configure the Ollama provider + models + a custom primary agent as above.
  2. Pipe a raw ACP handshake into opencode acp:
    • send JSON-RPC initialize, then session/new with cwd = the project directory (machine has Ollama running).
  3. Inspect configOptions in the session/new response.

Expected id: "model" options include the Ollama models and currentValue equals the configured default; id: "mode" includes the custom agent.

Actual: 45 built-in models only, default falls back to google/gemini-3.8-flash, modes are build + plan.

Screenshot and/or share link

N/A — raw ACP probe output available on request.

Operating System

macOS (Darwin, arm64)

Terminal

Bare stdio JSON-RPC probe (also reproducible from Zed's agent panel / any ACP client)


Investigation notes (bisected v2.0.3 → v2.0.4, v2 branch):

  • refactor(protocol): remove plugin activation wait — deletes client.plugin.awaitActivation({ location }) from loadCatalog() in packages/cli/src/acp/service.ts; replaced by a 5 s poll that exits on the first enabled model, so config-sourced providers lose the race.
  • refactor(core): split provider and model registries (#48901) — provider visibility is gated on provider.activation === "enabled"; the built-in Ollama plugin only sets that after an async GET /api/tags discovery round, which never completes before the ACP poll exits.

Related: #49630 (custom providers missing in ACP catalog), #34743, #42835, #46311.

Activity

  1. draftseventhirty commented on Sep 21, 2026

    @draftseventhirty

    i have this issue as well on 2.0.12 when trying to access a custom openAI provider through Zed

  2. holny commented on Sep 22, 2026

    @holny
    Contributor

    I'll take this. The regression is between 2.0.3 and 2.0.4 — configOptions / catalog loses user config when the session comes in via ACP. The v2.0.4 boundary lines up with the session/prompt module split, so I expect the leak to be in the ACP-driven session bootstrap (likely somewhere around packages/opencode/src/acp/... plus the session/new handler in core) where the per-location config resolution was wired to the runtime location.

    Plan: locate the session/new request handler for ACP, check what config it loads vs what the non-ACP server bootstrap loads, and either thread the missing entries or fix the location resolution. I'll add a regression test that drives a session/new with a non-default provider + a custom agent and asserts configOptions contains them.

    Fixes #50236

  3. munir-weave commented on Sep 22, 2026

    @munir-weave

    Confirming on 2.0.5 and 2.0.14 (macOS arm64) with a plain config-only provider, with no discovery plugin involved:

    "provider": { "weave": { "npm": "@ai-sdk/openai", "options": { "baseURL": "https://…/v1", "apiKey": "…" },
      "models": { "auto": { "name": "Auto", "limit": { "context": 128000, "output": 32000 } } } } },
    "model": "weave/auto"

    Polling GET /api/model on a fresh opencode serve shows the catalog filling in over ~50ms: 0 → 74 (raw Zen) → 7 (free Zen) → 8 (+weave/auto) on 2.0.14, and 0 → 7 → 8 on 2.0.5. /api/model/default reads as a Zen model until the last stage, so the "default model must be present" check in the catalog poll doesn't hold it either. In 24 ACP session/new probes across both versions, weave/auto was never in configOptions. After ~2s, /api/model for the same directory lists it. On 2.0.3 the same config works 9/9.

    Two details that make this hard to work around client-side:

    • The catalog is cached per cwd for the connection's lifetime, and no config_option_update is sent when models change.
    • session/set_config_option with model=weave/auto is rejected with model not found, so a client can't select the provider even if it discovers it some other way.

    Things that don't help: disabled_providers: ["opencode"] or enabled_providers: [...] (applied in the same late stage), or delaying session/new.

    This also breaks Conductor, which bundles opencode 2.0.5 as its managed ACP harness, so config-defined providers never show up in its model picker. #50619's approach of waiting for activation before reading the catalog looks like the right fix. Happy to test it against this setup.

  4. igorkis-scrts commented on Sep 24, 2026

    @igorkis-scrts

    Have the same issue as well - it completely ignores my llama-server models and shows only cloud and lmstudio ones (and I'm using neither). Before V2 migration everything was fine.
    Image

  5. HuHong-Tao commented on Sep 25, 2026

    @HuHong-Tao

    Confirming this still reproduces on 2.0.16 (npm @opencode/cli, macOS arm64 — installed 2026-09-25). The highest version reported in this thread so far was 2.0.14, so the regression is still present in the latest release.

    Setup here is a config-only OpenAI-compatible provider (no discovery plugin, no Ollama, no auth.json entry): provider.<id> in ~/.config/opencode/opencode.json with npm: "@ai-sdk/openai-compatible", inline options.apiKey/baseURL, and an explicit models map.

    Observed on 2.0.16:

    • ACP session/new → configOptions[model].options contains zero models from that provider, and currentValue falls back to a built-in (opencode/*, or xiaomi/* on a setup with other credentials stored).
    • session/set_config_option with model=<provider>/<model> → -32602 Invalid params: model not found: <provider>/<model> (data.providerId is parsed correctly), so a client cannot select the provider even when it knows the id.
    • Same build, same config: opencode models lists the provider's models and opencode run --model <provider>/<model> answers normally — ACP catalog only.
    • List size is not stable across runs with an identical config: 7 / 8 / 17 / 19 / 20 options observed in isolated XDG_* sandboxes, and 95 (= providers from the models.dev catalog with stored credentials) on a long-lived setup. That matches the "poll exits before config-sourced providers activate" diagnosis above.

    Client impact worth noting: an ACP client (Obsidian's Grimoire plugin) renders the model not found error as "The selected OpenCode model is unavailable for this account or plan. Choose another model, then retry.", which sends users chasing billing/plan issues instead of this bug.

    Client-side workarounds: none for config-declared providers. Selecting a model that is in the catalog works in the same session, and the CLI is unaffected.

  6. HuHong-Tao commented on Sep 25, 2026

    @HuHong-Tao

    Extra data point that isolates this to the ACP catalog loader rather than config loading: on 2.0.16 (macOS arm64), with an intact config containing a config-declared OpenAI-compatible provider, the HTTP API and ACP disagree at the same moment:

    • GET /api/model → 38 models including the provider's 5 ({opencode: 8, xiaomi: 6, openai: 15, deepseek: 4, <custom>: 5}), and GET /api/provider lists the custom provider with activation: "enabled".
    • opencode acp → session/new immediately after → configOptions[model].options = 95 models from github-copilot / openai / xiaomi only, custom provider absent (reproduced twice, ~2 min apart).

    So config loading and provider activation do complete for the server; only the ACP catalog snapshot misses them (or misses them most of the time — the same probes returned 7 / 8 / 17 / 19 / 20 / 95 entries depending on run, which is why I think the poll-exit timing is the deciding factor rather than any file/config difference).

    Worth noting for anyone debugging: opencode reload or a service restart does not change the ACP result, but the server's /api/model picks the provider up right away.

  7. cristoslc commented on Sep 26, 2026

    @cristoslc

    Adding two data points that extend @munir-weave's timing analysis and sharpen the failure signature for @holny's fix (repro details below, raw JSON-RPC against opencode acp, no third-party client — consistent with the snapshot-timing finding: the catalog the ACP layer serves looks like a pre-config-merge snapshot, which is why only bare upstream ids and the generic effort ladder appear).

    1. Set-accepts / prompt-rejects split. session/set_config_option with a generic effort value (high from the advertised none/minimal/low/medium/high/default ladder) is accepted and stored on the session — then the prompt-time resolver rejects the same ref against the config-merged catalog:

    → set_config_option { configId: "model",  value: "ollama-cloud/deepseek-v4.1-flash" }   ← ok
    → set_config_option { configId: "effort", value: "high" }                               ← ok
    → prompt { ... }
    ← -32603 "Internal error: Variant unavailable for ollama-cloud/deepseek-v4.1-flash: high"
       data: { "service": "session", "errorName": "provider.no-route" }
    

    So a host that picks only from advertised options — protocol-correct behavior — still produces a session that fails at first prompt, surfaced as a generic provider.no-route error rather than invalid-params. Same root as #50378. A regression test for the fix should assert not only that configOptions contains the config-defined entries, but that a set-then-prompt using advertised values succeeds.

    2. A verified workaround (no fix required): make the config coincide with what the pre-merge snapshot advertises — bare upstream model ids, variant ids renamed to the generic ladder, same reasoningEffort values:

    {
      "providers": {
        "ollama-cloud": {
          "models": {
            // was "deepseek-v4.1-flash:cloud" with variants thinking/low-effort/max-effort
            "deepseek-v4.1-flash": {
              "limit": { "context": 1000000, "output": 32768 },
              "variants": [
                { "id": "low",  "settings": { "reasoningEffort": "low" } },
                { "id": "high", "settings": { "reasoningEffort": "high" } },
                { "id": "max",  "settings": { "reasoningEffort": "max" } }
              ]
            }
          }
        }
      }
    }

    Verified end-to-end on 2.0.16: after this change, sessions selected entirely from advertised options resolve and prompt.

    Environment: opencode 2.0.16, macOS 15 arm64 (official bun binary), config-only OpenAI-compatible providers (aisdk:@ai-sdk/openai-compatible, suffixed ids like deepseek-v4.1-flash:cloud, custom variant names thinking/low-effort/max-effort — all resolvable in the TUI). The original failure was observed through an ACP desktop host as provider.no-route cards at first prompt.

  8. strk-ai-agent commented on Sep 26, 2026

    @strk-ai-agent

    Adding a data point that may explain why this looks like a v2 login requirement, since that is the conclusion the behavior invites — it is not one.

    On 2.0.16, with no Zen credential anywhere (no OPENCODE_API_KEY in the environment, nothing Zen-related in opencode auth list, global ~/.config/opencode/opencode.jsonc containing only $schema):

    • opencode run -m opencode/space-bunny-free "say hi" in a project directory → answers normally, as > <agent> · space-bunny-free.
    • opencode acp in that same directory → the model session option contains zero opencode/* entries, and a new session comes up on the "newest available supported model" from an unrelated provider instead.

    So the free Zen models are reachable without any credential (they also answer a plain POST https://opencode.ai/zen/v1/chat/completions with no Authorization header), but the ACP catalog never lists them.

    Setting OPENCODE_API_KEY to a dummy value makes the opencode/* and opencode-go/* entries appear in the catalog and the configured model pin get honored — session/new then reports currentModelId=opencode-go/space-bunny-free — but every prompt fails with:

    {"code":-32000,"message":"Authentication required: provider authentication required"}
    

    even though that same model is the one that just worked unauthenticated over plain HTTP. That points at a client-side credential gate in the catalog/availability path rather than at anything the endpoint requires.

    Asking for either of these would make the failure much easier to act on:

    1. treat a provider as available when the model is actually reachable, not only when a credential is stored (or let the catalog include keyless-reachable free models), and
    2. when a configured model is dropped for a fallback, say why in the session result or a log line, so a working pin failing to apply does not look like a policy change.

    Context: hit this through acp-chat-sloppy-bridge 0.7.0 driving opencode acp over stdio, where the user-visible symptom is "the bot answers its own messages but the agent never replies".

  9. lkshrk commented on Sep 27, 2026

    @lkshrk

    Still reproduces on 2.0.18 (npm @opencode/cli-linux-x64, Linux x64). The setup is opencode acp over stdio with a config-only OpenAI-compatible provider and a top-level model. Some details that may matter for #50619 and for anyone who needs a workaround now:

    • It's intermittent, not deterministic. Across our runs, about half of the fresh opencode acp processes came up with a Zen-only catalog. In those, currentValue was an opencode/*-free model and none of the config provider's models were listed. The other half came up correct.
    • A bad snapshot sticks for the life of the process. catalog(cwd) in packages/cli/src/acp/service.ts caches the loadCatalog promise per cwd. Every later session/new or session/load in the same process reuses the stale catalog. session/set_config_option for a config model then fails with Invalid params: model not found: <provider>/<model>, and the prompt runs on the Zen fallback. Retrying the session in the same process never recovers.
    • experimental.policies doesn't help. We added provider.use deny * and allow <provider>. It removes the Zen models from GET /api/model once the process is warm, but the ACP snapshot is taken before the policy transform applies, so the same race still happens.
    • Workaround (client side): after session/new/session/load, check that configOptions[model].options contains the expected models. If they're missing, kill the process and spawn a new one. In practice it converges within a few restarts, about 1 s each. The rejected attempts leave empty sessions behind in the session list.

    Update — a better workaround without respawning: the catalog snapshot happens right after the first request boots the location. The config provider arrives 0.3–0.6 s later, and the bundled models.dev catalog already has Zen models, so the poll exits at once. Emptying the built-in catalog removes the race:

    echo '{}' > /path/to/models.json
    OPENCODE_MODELS_PATH=/path/to/models.json OPENCODE_DISABLE_MODELS_FETCH=1 opencode acp

    With no built-in models, loadCatalog keeps polling until the config provider shows up. Result: 8/8 fresh processes and 4 concurrent ones all came up with the correct catalog and default model, with no retries. Obviously only an option when every model comes from config providers.

  10. iamhenry commented on Sep 28, 2026

    @iamhenry

    i have this issue too. plz fix it

  11. Paul-van-Klaveren commented on Sep 29, 2026

    @Paul-van-Klaveren

    I am required to use OpenCode v2 (acp) in Xcode 27 set up with a locally-hosted offline-only Ollama provider and a model configured.
    If OpenCode acp cannot be set up with locally-hosted offline-only models, and instead uses the free models, I cannot use it at all.
    ~/.config/opencode/opencode.jsonc:

    {
      "$schema": "https://opencode.ai/config.json",
      "model": "ollama/ornith:35b-q8_0",
      "providers": {
        "ollama": {
          "settings": {
            "baseURL": "http://192.168.12.345:12345/v1"
          }
        }
      }
    }

    In Xcode 27, the opencode executable is set, and the argument acp is provided; otherwise nothing is set — no environment variables. I'm keen to learn if there is any workaround, e.g. via the environment variables instead of via the config file, to get opencode acp to use Ollama.

  12. nexxeln commented on Sep 30, 2026

    @nexxeln
    Contributor

    Fixed by #52286 (merged in 1844c11): ACP no longer pins agent/model from an early catalog snapshot on session/new, and it refreshes the catalog (pushing config_option_update / available_commands_update) when plugins finish loading config agents, providers, and commands. It also reloads the catalog before rejecting an unknown model or mode in set_config_option / set_mode. Please reopen if you still see this on a release that includes the fix.

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