Repository navigation
Conversation
…efault When `model` names a provider that was never registered, for example because its config entry failed the schema, Model.default returned the first available model, so prompts and tool calls went to the built-in free provider. Resolve no default in that case. Registered providers that are disabled or unavailable keep the existing fallback. Fixes anomalyco#58909
There was a problem hiding this comment.
This fixes the reported case. On the base, the config from #58909 ran on opencode/big-pickle and sent the request to the OpenCode endpoint. With this change the run stops with No model is available for session … and nothing is sent. The core model, catalog, resolver, session-runner and config tests pass, and tsc -b is clean.
Public API: no signatures change. Model.default (behind the plugin host and the server's model.default route) can now return nothing when the configured provider isn't registered. The type already allowed that.
One behaviour change is worth deciding on. The check is "is the provider in the provider records?", and providers removed by disabled_providers, enabled_providers or a provider.use policy are dropped from the records too. So model: anthropic/… together with disabled_providers: ["anthropic"] now hard-fails, where it used to fall back (I confirmed both). A typo or stale provider ID in model hard-fails the same way. That may be the intended safe behaviour, but it contradicts the description ("disabled providers keep the fallback" is only true for activation: "disabled"). And because the error names neither the session's model nor the configured provider/model, users hitting it won't know what to fix. Please either say that this is intended, or limit the check to providers that were dropped for invalid config. Including the configured model in the error would make either choice much easier to act on. See the inline comment.
Related: #55150
| if (!requested) return value.available.find(supportsText) | ||
| // A provider that was never registered (for example, its config entry failed the schema) resolves to nothing, | ||
| // so prompts never go to a provider the user did not choose. Registered but unavailable providers still fall back. | ||
| if (!(yield* providers.snapshot()).records.has(requested.providerID)) return undefined |
There was a problem hiding this comment.
records also loses providers removed by disabled_providers, enabled_providers and provider.use policies (config/plugin/policy.ts calls providers.remove). It isn't only schema-rejected entries. With model: anthropic/claude-sonnet-4-5 and disabled_providers: ["anthropic"], the base falls back to opencode/big-pickle, and this change stops with No model is available for session …. A typo in model behaves the same way. Refusing to run on another provider may well be the right call here, but it's broader than the description says, and the error doesn't mention the configured provider/model, so users have nothing to go on. Can you confirm this is intended (and ideally name the configured model in the error)? Otherwise, limit the check to providers dropped for invalid config.
There was a problem hiding this comment.
Intended. In each of those cases the user named a model, and sending the prompt to another provider is exactly what #58909 is about, so stopping is the safer result. The description was wrong about this and is now fixed, along with the code comment. The error now names the configured model, Model unavailable: probe/probe-1, the same as --model, so it is clear which entry to fix (0df1aeb).
When no model resolves and config names a default, fail with `Model unavailable: <provider>/<model>`, as `--model` does, instead of `No model is available for session …`, so the user knows which entry to fix. Also document that providers removed by `disabled_providers` or a `provider.use` deny, and unknown provider IDs, no longer fall back.
There was a problem hiding this comment.
Both points from my earlier review are resolved:
- You've confirmed that stopping for providers removed by
disabled_providers,enabled_providersorprovider.useis intended, and the description and code comment now say so. - The session error now names the configured model. With no
modelconfigured, the oldNo model is availableerror still applies.
I re-ran the config from the issue at this commit. It now fails with Model unavailable: probe/probe-1 and never reaches another provider. The disabled_providers case fails with Model unavailable: anthropic/claude-sonnet-4-5. The core model, catalog, resolver, session-runner and config tests pass, and tsc -b is clean.
Public API: unchanged. Model.configured() is only added to the internal core Model service; the plugin API and HTTP routes are unchanged.
No further concerns.
Related: #55150
Issue for this PR
Closes #58909
Type of change
What does this PR do?
When a provider entry in
opencode.jsonfails the config schema, the provider is never registered, butmodelstill names it.Model.defaultthen returned the first available model, so the prompt and its tool calls went to the built-in free provider without telling the user.Now a configured default whose provider is not registered resolves to nothing, and
ModelResolver.fallbackno longer picks another model for it. The session fails withModel unavailable: probe/probe-1, the same message--model probe/probe-1gives, so the user can see which entry to fix.This applies to every configured provider that is not registered, not only schema-rejected entries: providers removed by
disabled_providers/enabled_providersor aprovider.usedeny, and an unknown or misspelled provider ID, also stop instead of falling back. That is intended: in each case the user named a model, and silently sending the prompt elsewhere is the problem in #58909. Registered providers that are not active (activation: "disabled", or no connection) keep the existing fallback (covered byignores a configured default on a disabled provider).Model.configured()is a new method on the internal CoreModelservice; the plugin API and HTTP routes are unchanged.Model.default(behind themodel.defaultroute) can now return nothing in these cases, which its type already allowed.How did you verify your code works?
packages/core/test/model-free.test.tscoveringModel.default,ModelResolver.fallback/resolveand the runner error. It fails ondev, and fails if either the resolver or the runner change is reverted.packages/coresuite passes (7135 pass, 0 fail) andbun typecheckpasses.XDG_*dirs and no network. Ondev:> build · big-pickle, then retries againstopencode.ai. With this change:Error: Model unavailable: probe/probe-1.Screenshots / recordings
N/A
Checklist
Added by the opencode bot (AI-generated).
Related: #55150