Repository navigation
Provider config overrides npm and baseURL asymmetrically, producing impossible endpoints #28
Description
Activity
- addedbugSomething isn't workingSomething isn't workingneeds-triageMaintainer needs to evaluateMaintainer needs to evaluate
on Aug 16, 2026 Corrected root cause (supersedes the analysis in the issue body)
The asymmetry is real and the diagnosis in the title is right, but it is not in the v2 config/Catalog code. It is in the legacy provider stack,
packages/opencode/src/provider/provider.ts, which is whatrunand normal prompting actually execute. I confirmed by instrumentation thatSessionRunnerModel.fromCatalogModel(the v2 Catalog consumer) is never called duringopencode run.The mechanism
packages/opencode/src/provider/provider.ts:1425-1448:for (const [providerID, provider] of configProviders) { const existing = database[providerID] const parsed: Info = { ... options: mergeDeep(existing?.options ?? {}, provider.options ?? {}), // provider-wide models: existing?.models ?? {}, // catalog models kept as-is } for (const [modelID, model] of Object.entries(provider.models ?? {})) { // npm read ONLY here const apiNpm = model.provider?.npm ?? provider.npm ?? existingModel?.api.npm ?? modelsDev[providerID]?.npm ?? ...
provider.options— which carriesbaseURL— is merged into the provider and therefore applies to every model.provider.npmis only reachable inside theprovider.modelsloop. With nomodelsdeclared in config, that loop body never runs, sonpmis dead code and the catalog's package survives on every model.
resolveSDKthen prefersoptions.baseURLovermodel.api.url(provider.ts:1698-1719) and picks the SDK factory byBUNDLED_PROVIDERS[model.api.npm](provider.ts:106-134, 1770-1778). Catalog package + user host = the impossible endpoint;@ai-sdk/anthropicappends/messages.Reproduction, on the real config
Instrumented
resolveSDK,MINIMAX_API_KEYset to a dummy value.A — the config as filed (
npm+options.baseURL, nomodels):DBG model=MiniMax-M3 npm=@ai-sdk/anthropic catalogUrl=https://api.minimax.io/anthropic/v1 baseURL=https://api.minimax.chat/v1 Error: 404 Page not foundnpmdropped,baseURLapplied →https://api.minimax.chat/v1/messages, matchingmetadata.urlin the durable record.B — byte-identical except for one added line,
"models": { "MiniMax-M3": {} }:DBG model=MiniMax-M3 npm=@ai-sdk/openai-compatible catalogUrl=https://api.minimax.io/anthropic/v1 baseURL=https://api.minimax.chat/v1 Error: invalid api key (2049)npmnow honoured, coherent pair, endpoint reached — the error is auth (expected, the key was a dummy), not 404.So the override is not merely ignored: whether
npmapplies at all depends on whether the same block also declaresmodels, which nothing documents or reports.Workaround for anyone hitting this now
Declare the models you use under the provider block;
npmthen applies:"minimax": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": "https://api.minimax.chat/v1" }, "models": { "MiniMax-M3": {} } }
Notes on scope
- The earlier hypothesis that the config was edited after the failure is wrong — the file mtime predates the failure, and config A above reproduces the 404 from the file as filed.
- PR fix(core): reject v2 providers npm that conflicts with the resolved package #30 fixes a genuinely analogous asymmetry in the v2
providersschema, but that path is not executed byrun, so it does not fix this issue. It has been retitled and no longer claims to close this. - The
provider.npm-only-inside-the-models-loop behaviour is the fix site. Note that "override the host, keep the catalog's SDK" is a legitimate use (self-hosted proxies), so it cannot simply become an error — the fix is to makeprovider.npmapply provider-wide the wayprovider.optionsalready does.
- added a commit that references this issue
on Aug 16, 2026 - removedneeds-triageMaintainer needs to evaluateMaintainer needs to evaluate
on Aug 16, 2026 - added 2 commits that reference this issue
on Aug 17, 2026
What
A user provider block can override
options.baseURLbut notnpm. The catalog'snpmwins while the user'sbaseURLwins, so the two get combined into a request that cannot succeed anywhere.Reproduction, from a real session
Config in
~/.config/redcode/opencode.jsonc:models.dev catalog for the same provider:
What actually went out, from the durable error record (
metadata.url):/v1/messagesis the Anthropic path, so the SDK came from the catalog; the host came from the user config. Neither source describes that combination.Probed each candidate directly — 401 means the route exists and only auth was missing, 404 means it does not exist:
Both coherent pairs work. Only the mixture fails.
Why it matters
The failure is silent and misattributed. Nothing reports that the
npmoverride was dropped, so the user reasonably concludes their endpoint is wrong — it is not. Both halves of their intent were individually reasonable.Possible directions, not a decision
npmfrom user config the wayoptionsalready is, ornpmis supplied and cannot be applied, ornpmandbaseURLand fail loudly at resolution rather than at request time.Whichever way, the asymmetry should stop being silent.