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
config: model capabilities requires tools — missing field silently skips whole provider (undocumented) #50340
In native V2 config, a model entry with capabilities that omits tools fails validation, and the whole provider entry is silently skipped: its models disappear from selection with only a server log line
— no indication which field is missing or that the provider was dropped. Reproduced on 2.0.11: adding "tools": true to every capabilities object fixed it (nothing else changed).
The docs show capabilities examples that always include tools, but none of /v2/docs/models, /v2/docs/providers, or /v2/docs/migrate-v1 state that tools is required. The migrate-v1 guide maps modalities → capabilities.input/output and doesn't mention adding tools, so a converted V1 config hits this trap.
Suggested fix: make tools optional with a default, or document it as required and emit a field-level diagnostic (e.g. $.providers.<id>.models.<model>.capabilities.tools) instead of skipping the provider with a generic warning.
Plugins
local plugin: ~/.config/opencode/plugins/openviking
Confirmed in the current v2 sources: Capabilities declares all three fields required (packages/schema/src/model.ts:86-96 — tools, input, output), while the config overlay is capabilities: Capabilities.pipe(optional) (packages/schema/src/config/provider.ts:75). So a hand-written capabilities object that only sets input/output fails to decode, and the normalizer records the failure at the enclosing path.
One correction to the report, measured rather than read from the guides: a converted V1 config does not hit this. packages/core/src/v1/config/migrate.ts:323-331 builds the object as
i.e. it fills every field from Capabilities.default() (tools: true, input: ["text","image"], output: ["text"]). The trap is specific to hand-written V2 capabilities objects.
Where the generic diagnostic comes from: decodeValue/decodeEncoded in packages/core/src/config/normalize.ts decode with Schema.decodeUnknownOption and report invalid(path, diagnostics) — "skipped malformed recognized value" — at the enclosing path. Option is None on failure, so the ParseError (which carries the offending field path and message) is discarded before anything can log it, which is why the warning names $.providers.<id> and nothing deeper.
Plan: decode with decodeUnknownEither there and carry the first issue's path/message into the diagnostic, so the log names the actual field (e.g. $.providers.<id>.models.<model>.capabilities.tools) instead of the provider. Behaviour stays as it is — still skipped, no change to which configs load. Making the three capability fields individually optional (with Capabilities.default() as the fallback) is the other half of your suggestion and I would leave that to a maintainer, since it decides what a partial overlay means for a model whose catalog entry already sets input: ["text","image"]. I'll add the case to packages/core/test/config/normalization.test.ts.
Note for whoever reviews: packages/core/src/config/normalize.ts exists only on v2, so the PR will target v2, not dev.
Same root cause, new trigger (@#50756): an invalid legacy package id — @ai-sdk/openai-compatible (V1) instead of @opencode/ai/providers/openai-compatible (V2) — drops the entire provider with the identical terse diagnostic, no field detail. Full repro + config block in #50756. Together with this issue and #49912 that's three unrelated malformed values (missing capabilities.tools, bad package id) all collapsing to 'skipped malformed recognized value' at the provider root — strong evidence the diagnostic should name the offending sub-path and value (ideally with a migration hint for known legacy ids like @ai-sdk->@opencode/ai).
Thanks for the detailed report! I think this is a duplicate of #51252, which reported the same thing: a native providers.<id> model whose capabilities sets only input/output caused the whole provider to be dropped with skipped malformed recognized value.
It was fixed in #51365, which shipped in v2.0.22. Model capabilities in native provider config can now be partial. Fields you leave out fall back to the base model's values or the defaults, so tools defaults to true. Updating from 2.0.11 to a current release should let your original config (without "tools": true) load.
Possible duplicates and related issues, ordered by likelihood:
The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different, for example if it still happens on v2.0.22 or later.
We haven't heard back, so I'm closing this as a duplicate of #51252. That issue covers the same problem: a native provider was dropped when a model's capabilities left out tools. #51365 fixed it in v2.0.22, and leaving out tools now defaults it to true. Updating from 2.0.11 should let your original config load.
The point about the generic skipped malformed recognized value diagnostic is tracked in #58909 and #54786. If the provider still gets dropped on v2.0.22 or later, please comment here or open a new issue with your config and the version.
Activity
argszero commentedon Sep 22, 2026
I'd like to work on this.
Confirmed in the current
v2sources:Capabilitiesdeclares all three fields required (packages/schema/src/model.ts:86-96—tools,input,output), while the config overlay iscapabilities: Capabilities.pipe(optional)(packages/schema/src/config/provider.ts:75). So a hand-writtencapabilitiesobject that only setsinput/outputfails to decode, and the normalizer records the failure at the enclosing path.One correction to the report, measured rather than read from the guides: a converted V1 config does not hit this.
packages/core/src/v1/config/migrate.ts:323-331builds the object asi.e. it fills every field from
Capabilities.default()(tools: true,input: ["text","image"],output: ["text"]). The trap is specific to hand-written V2capabilitiesobjects.Where the generic diagnostic comes from:
decodeValue/decodeEncodedinpackages/core/src/config/normalize.tsdecode withSchema.decodeUnknownOptionand reportinvalid(path, diagnostics)—"skipped malformed recognized value"— at the enclosing path.OptionisNoneon failure, so theParseError(which carries the offending field path and message) is discarded before anything can log it, which is why the warning names$.providers.<id>and nothing deeper.Plan: decode with
decodeUnknownEitherthere and carry the first issue's path/message into the diagnostic, so the log names the actual field (e.g.$.providers.<id>.models.<model>.capabilities.tools) instead of the provider. Behaviour stays as it is — still skipped, no change to which configs load. Making the three capability fields individually optional (withCapabilities.default()as the fallback) is the other half of your suggestion and I would leave that to a maintainer, since it decides what a partial overlay means for a model whose catalog entry already setsinput: ["text","image"]. I'll add the case topackages/core/test/config/normalization.test.ts.Note for whoever reviews:
packages/core/src/config/normalize.tsexists only onv2, so the PR will targetv2, notdev.fix(core): localize config normalization diagnostics to the failing f…
famewolf commentedon Sep 22, 2026
Same root cause, new trigger (@#50756): an invalid legacy
packageid —@ai-sdk/openai-compatible(V1) instead of@opencode/ai/providers/openai-compatible(V2) — drops the entire provider with the identical terse diagnostic, no field detail. Full repro + config block in #50756. Together with this issue and #49912 that's three unrelated malformed values (missing capabilities.tools, bad package id) all collapsing to 'skipped malformed recognized value' at the provider root — strong evidence the diagnostic should name the offending sub-path and value (ideally with a migration hint for known legacy ids like @ai-sdk->@opencode/ai).opencodejr commentedon Oct 28, 2026
Thanks for the detailed report! I think this is a duplicate of #51252, which reported the same thing: a native
providers.<id>model whosecapabilitiessets onlyinput/outputcaused the whole provider to be dropped withskipped malformed recognized value.It was fixed in #51365, which shipped in v2.0.22. Model
capabilitiesin native provider config can now be partial. Fields you leave out fall back to the base model's values or the defaults, sotoolsdefaults totrue. Updating from 2.0.11 to a current release should let your original config (without"tools": true) load.Possible duplicates and related issues, ordered by likelihood:
capabilitiesomitstools), closed as fixed by fix(core): accept partial model capabilities in native provider config #51365opencode.jsonis silently ignored, with no visible errorThe bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different, for example if it still happens on v2.0.22 or later.
opencodejr commentedon Oct 29, 2026
We haven't heard back, so I'm closing this as a duplicate of #51252. That issue covers the same problem: a native provider was dropped when a model's
capabilitiesleft outtools. #51365 fixed it in v2.0.22, and leaving outtoolsnow defaults it totrue. Updating from 2.0.11 should let your original config load.The point about the generic
skipped malformed recognized valuediagnostic is tracked in #58909 and #54786. If the provider still gets dropped on v2.0.22 or later, please comment here or open a new issue with your config and the version.