You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
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.
Make sure the plugin has no model cache ($XDG_CACHE_HOME/opencode-litellm/ empty).
opencode run --standalone -m litellm/<model> "hi" with stdin closed.
Model unavailable: litellm/<model>; running again gives the same error (the process exits before the plugin's background discovery finishes and writes its cache).
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)
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.
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.
Activity
opencode-agent commentedon Oct 20, 2026
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
setupruns in one batch, and the config-declaredproviders.*are added by an internal plugin in that same batch. Those changes only take effect, andprovider.updatedonly fires, once the batch has finished, soctx.provider.list()can't seelitellmwhile yoursetupis running. The plugin docs suggest loading provider data insidesetupand callingctx.provider.reload()later for updates. Discovery that starts aftersetupisn't somethingopencode runwaits for. Plugins also can't currently read the config fromctx, so a plugin can't getproviders.litellm.settingsduringsetupeither. That's why the plugin-options route works and theprovidersroute 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-shotrunwaits for, capped by the activation timeout. Maintainers will consider it.Related issues:
run --standalonerejects a model before the catalog refresh finishes). The fix proposed there adds a readiness latch, close to your second suggestion./api/modelbehaves while plugins reload.provider.transformcallbacks not running.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.