Repository navigation
Description
Summary
Declaring a custom provider under the current top-level providers key is accepted by the config loader, and the provider's models are registered (they appear in opencode models and in the model picker), but the two fields that actually make the provider usable - npm and options.baseURL - are silently dropped. Every request against such a model then fails in under two seconds with an empty package name.
Declaring the same provider under the deprecated singular provider key works with an otherwise identical config.
No warning, no config error, nothing at startup. The only visible symptom appears at request time, and it points away from the cause.
Environment
- opencode v2.0.32
- Windows, PowerShell 5.1
Repro
opencode.json in an otherwise empty project:
{
"$schema": "https://opencode.ai/config.json",
"model": "example-provider/some-model",
"providers": {
"example-provider": {
"npm": "@ai-sdk/openai-compatible",
"env": ["EXAMPLE_API_KEY"],
"options": {
"baseURL": "https://example.test/v1",
"timeout": 480000,
"chunkTimeout": 180000
},
"models": {
"some-model": {
"name": "Some Model (example provider)",
"tool_call": true,
"limit": { "context": 1048576, "output": 65536 }
}
}
}
}
}With EXAMPLE_API_KEY set in the environment:
opencode models --standalone- listsexample-provider/some-model. Looks configured.opencode debug config- the provider'snpmandoptions.baseURLare absent/empty in the resolved document.opencode run --standalone -m example-provider/some-model "hi"- fails:
provider.no-route: Unsupported package for example-provider/some-model:
Note the trailing colon and nothing after it: an empty package name.
- Change the single key from
"providers"to"provider"and repeat step 3 - the request succeeds.
Evidence that the key is the cause
Same config file, one variable changed at a time:
| Variant | Result |
|---|---|
"provider" (singular, deprecated) |
request succeeds |
"providers" + npm at provider level |
fails, empty package name |
"providers" + npm under each models.<id>.provider |
fails identically |
"providers" + api instead of npm |
fails identically |
"providers" + per-model provider.npm only |
fails identically |
control: config round-tripped through ConvertTo-Json -Depth 20, singular key kept |
request succeeds |
The control rules out serialisation and field placement: identical bytes apart from the key name.
Expected
Either
"providers"wires the provider up the same way"provider"does, or- declaring a provider that cannot be resolved produces a startup/config error and is not advertised in
opencode models.
Registering the models while discarding the connection fields is the worst of the options, because every diagnostic (opencode models, the picker, the config dump) reports success.
Possible cause
providers may be intended as an override layer for already-registered providers, while provider was the V1 registration key. If so, the schema does not convey that distinction: both accept npm, env, options and models, so a new provider declared under providers parses cleanly and does nothing.
A narrow check that would distinguish the two: does an entry in providers for a provider that is not already registered get a package from npm? Under the current behaviour it does not.
Activity
opencode-agent commentedon Oct 18, 2026
Thanks for the detailed report. I reproduced this on v2.0.32, and it still happens on the latest
dev(da9d436).What I ran (the script below does the same, against a local mock OpenAI-compatible server):
"providers",npm,options.baseURLand the model'stool_callare dropped without any warning.opencode modelsstill listsexample-provider/some-model,opencode debug configshows the provider with onlyenvandmodels, andopencode runfails withUnsupported package for example-provider/some-model:."provider"(singular), the same entry is converted to the v2 field names automatically, and the request succeeds.The v2
providersschema uses different field names:packageinstead ofnpm,settingsinstead ofoptions, andcapabilities.toolsinstead oftool_call. Unknown keys are currently ignored without a warning. Until this is addressed, this form worked for me underproviders:Your point still holds: the config should either warn about or migrate the v1 field names instead of registering a provider that can't work. The maintainers will look at that part.
Reproduction script (repro.sh)
Run it:
bash repro.shargszero commentedon Oct 18, 2026
I'd like to work on this. I read the code path and can confirm the mechanism, which is narrower than it first looks.
Why
providerworks andprovidersdoes not. Both keys are real and both are read by the same normalizer, but by different routes:packages/core/src/config/normalize.ts:168sends the legacy singularproviderthroughmigrateProviders, which runs each entry throughConfigProviderV1.Infoand thenConfigMigrateV1.migrateProvider. That migration is where the V1 spellings are translated:packages/core/src/v1/config/migrate.ts:124mapsnpmtopackage, and:126-128mapsoptions(andapi) ontosettings/headers/body.packages/core/src/config/normalize.ts:169decodes the newproviderskey straight againstConfigProvider.Info, whose fields arepackage,settings,headers,body,models,env,name,canonical,compatibility(packages/schema/src/config/provider.ts). There is nonpmand nooptionsin that struct.The decoder is configured with
onExcessProperty: "ignore"(normalize.ts:44), sonpmandoptions.baseURLare dropped without a word — which is exactly the "silently" in the title. The models are still registered becausemodelsis a valid field, which is whyopencode modelsadvertises a provider that cannot route, and why the failure surfaces later as an empty package name frompackages/core/src/model-resolver.ts:37(Unsupported package for <provider>/<model>:—packageundefined).What I intend to change. Fix the silence for the
providerskey the way the legacy key already handles its unsupported fields:normalize.ts:605 diagnoseProviderUnsupportedreports V1-only keys through the existingunsupporteddiagnostic kind, andproviderscurrently gets no equivalent. I would report the V1 spellings on the native path as well —npmat provider level pointing atpackage,optionspointing atsettings/headers/body, and the per-modelprovider.npm— so the warning names the replacement instead of the field vanishing. Diagnostics are already surfaced as log warnings bypackages/core/src/config.ts:123, so the user sees the reason at load time rather than an empty package name at request time.That changes reporting only: no config field is added, removed or re-interpreted, and no provider that resolves today stops resolving.
Where I would want a maintainer's word. Your "Expected" section offers two other outcomes, and both are config-surface decisions rather than bug fixes, so I do not want to pick one unasked:
npm/optionsunderprovidersas aliases ofpackage/settings, which would make the V1 spelling work everywhere and make the migration's mapping redundant for new files; orIf either is preferred over the diagnostic, say so and I will build that instead.
rekram1-node commentedon Oct 22, 2026
Thanks for the report. The config is mixing V1 and V2 shapes.
providers(plural) is the V2 key and expects V2 field names:packageinstead ofnpm,settingsinstead ofoptions, andcapabilities.toolsinstead oftool_call. Underproviders,npm,optionsandtool_callaren't valid fields, so the provider ends up with no package or endpoint. The schema athttps://opencode.ai/config.jsonflags them as not allowed there.Either keep the V1 shape under the singular
providerkey, or rewrite the entry in the V2 shape. See https://opencode.ai/v2/docs/providers#custom for the format.OpenCode: снимаем всё, из-за чего провайдер не появляется в списке