Skip to content

config: model capabilities requires tools — missing field silently skips whole provider (undocumented) #50340

Closed as duplicate

Description

@e3322-pcsk9

Description

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

configuration normalization diagnostic path=$.providers.<id> kind=invalid action="skipped malformed recognized value"

— 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
  • opencode-antigravity-auth@latest, superpowers (git)

OpenCode version

2.0.11

Steps to reproduce

  1. In opencode.json, define a custom provider with a model that has:
"capabilities": { "input": ["text"], "output": ["text"] }
  1. Reload config (or restart)
  2. Observe: provider and its models are unavailable; server log shows path=$.providers.<id> kind=invalid action="skipped malformed recognized value"
  3. Add "tools": true to the capabilities object → provider loads, models selectable

Expected: tools defaults sensibly (or the requirement is documented), and any diagnostic names the actual missing field.

Screenshot and/or share link

n/a

Operating System

macOS (Darwin 25.6.0, arm64)

Terminal

Ghostty

Activity

  1. argszero commented on Sep 22, 2026

    @argszero
    Contributor

    I'd like to work on this.

    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

    const defaults = Model.Capabilities.default()
    const capabilities =
      info.tool_call !== undefined || info.modalities?.input !== undefined || info.modalities?.output !== undefined
        ? { tools: info.tool_call ?? defaults.tools, input: info.modalities?.input ?? defaults.input, output: info.modalities?.output ?? defaults.output }
        : undefined

    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.

  2. added a commit that references this issue on Sep 22, 2026
    0771faf
  3. famewolf commented on Sep 22, 2026

    @famewolf

    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).

  4. opencodejr commented on Oct 28, 2026

    @opencodejr

    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.

  5. opencodejr commented on Oct 29, 2026

    @opencodejr

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions