Skip to content

plugin: config-declared providers.* are not visible to plugins during setup, so one-shot opencode run resolves the model before plugin-registered models exist #57421

Open

Description

@zyc

Description

Summary

A plugin that discovers models and registers them through ctx.provider.transform / ctx.provider.reload() cannot make those models available to a one-shot command (opencode run) when the provider is declared under providers in the config. ctx.provider.list() does not contain config-declared providers while setup runs, they appear only after setup has returned (via provider.updated), and opencode run resolves the model before the plugin has published anything:

Error: Model unavailable: litellm/<model>

Long-lived sessions (TUI, opencode serve) are fine: the plugin reconciles on provider.updated and the models show up a moment later. The failure is specific to one-shot commands (scripts, CI, "test your setup" commands in guides) when the plugin has nothing cached yet.

Environment

  • OpenCode 2.0.32 and 2.1.0 (same behavior)
  • macOS, Node 22
  • Plugin: [email protected] (LiteLLM model discovery)

Repro

Config:

{
  "plugins": ["[email protected]"],
  "providers": {
    "litellm": {
      "package": "@opencode/ai/providers/openai-compatible",
      "settings": { "baseURL": "http://localhost:4000/v1", "apiKey": "{env:LITELLM_API_KEY}" }
    }
  }
}
  1. Make sure the plugin has no model cache ($XDG_CACHE_HOME/opencode-litellm/ empty).
  2. opencode run --standalone -m litellm/<model> "hi" with stdin closed.
  3. Model unavailable: litellm/<model>; running again gives the same error (the process exits before the plugin's background discovery finishes and writes its cache).
  4. Run opencode serve for a few seconds, stop it, repeat step 2: works (warm cache).

Evidence for the ordering

Inside setup, polling context.provider.list() every 100 ms for 3.5 s returned only the built-in providers the whole time, never the config-declared litellm. Right after setup returned, provider.updated fired and provider.list() then contained it. So a plugin cannot wait for the provider inside setup, and the host's ~5 s plugin-activation limit makes long waits unsafe anyway.

Declaring the same plugin through its own options ("plugins": [{ "package": "...", "options": { "baseURL": "..." } }]) does work cold, because the plugin then discovers during setup without needing the host's provider list. It is limited to one provider per plugin entry (two entries of the same package: only the last takes effect).

Expected behavior

Any of these would remove the race:

  • register config-declared providers.* before plugin setup runs, so ctx.provider.list() includes them there; or
  • let a plugin signal "still publishing providers" (or expose a readiness hook such as awaiting the first provider.updated), and have opencode run wait for that, with the existing activation timeout as the upper bound; or
  • document the intended ordering so plugin authors know discovery must be driven by plugin options for one-shot commands.

Related: #52618 (plugin reload wipes the provider registry), #51802 (provider.transform callbacks not executing on 2.0.18). I also filed the plugin-side view: yuseferi/opencode-litellm#46

Plugins

opencode-plugin-litellm

OpenCode version

2.0.32 and 2.1.0

Steps to reproduce

See the repro section above.

Operating System

macOS (Darwin arm64)

Terminal

Not relevant (reproduced with opencode run --standalone, no TTY)

Activity

  1. opencode-agent commented on Oct 20, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed write-up and the timing evidence. It makes the ordering easy to follow.

    From the code, this is how plugin activation currently works rather than a regression. Every plugin's setup runs in one batch, and the config-declared providers.* are added by an internal plugin in that same batch. Those changes only take effect, and provider.updated only fires, once the batch has finished, so ctx.provider.list() can't see litellm while your setup is running. The plugin docs suggest loading provider data inside setup and calling ctx.provider.reload() later for updates. Discovery that starts after setup isn't something opencode run waits for. Plugins also can't currently read the config from ctx, so a plugin can't get providers.litellm.settings during setup either. That's why the plugin-options route works and the providers route doesn't.

    So I'm treating this as a feature request for one of the options you listed: let plugins see config-declared providers during setup, or add a readiness signal that one-shot run waits for, capped by the activation timeout. Maintainers will consider it.

    Related issues:

    Until then, as you found, setting the discovery options on the plugin entry itself ("plugins": [{ "package": ..., "options": { ... } }]) is the way to make cold one-shot runs work.

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