Skip to content

invalid opencode.json is silently ignored #54786

Open

Description

@melato

Description

I entered an invalid ~/.config/opencode/opencode.json (or local opencode.json) and opencode started without complaining. This is important for new users (like me) who are trying to learn how to configure opencode. Mistakes are silently ignored.

The real issue for me is that my ollama provider in opencode.json is sometimes ignored, but without any indication of errors, how can I diagnose the configuration?

Plugins

No response

OpenCode version

v2.0.27

Steps to reproduce

  1. Download standalone CLI binary from https://opencode.ai/v2/docs
    I used x86, both glibc (with debian-13), and musl (with alpine 3.24.2).

  2. create ~/.config/opencode/opencode.json with invalid JSON content. I used "{".

  3. run "opencode models"

Result

A list of models from the opencode/ provider.

Expected result

An error indicating that opencode.json is invalid.

Additional info

I also tried opencode.jsonc

Screenshot and/or share link

No response

Operating System

alpine/3.24, debian/13.

Terminal

standard ssh

Activity

  1. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    Thanks for the clear report, @melato. I can reproduce this.

    With a clean config directory on v2.0.27 (Linux):

    1. Put just { in ~/.config/opencode/opencode.json.
    2. Run opencode models.

    It lists the built-in opencode/* models, exits 0, and shows no error or warning. A malformed project ./opencode.json behaves the same way. The script below reproduces it.

    It still happens on the latest dev (bcf23b6) with the global { case; I didn't run the project-file case there. From the code, the config loader notices the parse error but only writes it as a log warning and then skips the file. I couldn't find that warning in a log file during my test, so for now there's no reliable way to spot a rejected config. A file that parses but fails validation is also dropped without a message. That could explain why your Ollama provider is sometimes ignored: if part of the config is invalid, the whole file may be dropped.

    Related: #54109 (custom Ollama provider ignored) and #53744 (provider dropped without a message because of a schema mismatch).

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduces anomalyco/opencode#54786: an invalid opencode.json is silently ignored.
    # Usage: ./repro.sh            (uses `opencode` from PATH)
    #        OC_DEV=/path/to/repo ./repro.sh   (runs the CLI from a source checkout)
    set -u
    WORK=$(mktemp -d)
    export HOME="$WORK/home"
    export XDG_CONFIG_HOME="$HOME/.config" XDG_DATA_HOME="$HOME/.local/share" XDG_STATE_HOME="$HOME/.local/state" XDG_CACHE_HOME="$HOME/.cache"
    mkdir -p "$XDG_CONFIG_HOME/opencode" "$WORK/project"
    cd "$WORK/project"
    
    run() {
      if [ -n "${OC_DEV:-}" ]; then
        # run from packages/cli so its bunfig.toml (Solid JSX preload) also applies to the spawned server process
        (cd "$OC_DEV/packages/cli" && bun --conditions=browser src/index.ts "$@")
      else
        opencode "$@"
      fi
    }
    
    echo "== version: $(run --version 2>&1 | tail -1)"
    
    echo '{' > "$XDG_CONFIG_HOME/opencode/opencode.json"
    echo "== Case 1: global opencode.json contains just '{'"
    run models; echo "exit code: $?"
    
    rm "$XDG_CONFIG_HOME/opencode/opencode.json"
    echo '{ "this is": not json' > opencode.json
    if [ -n "${OC_DEV:-}" ]; then exit 0; fi  # case 2 needs the project as cwd
    echo "== Case 2: project ./opencode.json is malformed"
    run models; echo "exit code: $?"
    

    Run it: bash repro.sh

  2. r266-tech commented on Oct 12, 2026

    @r266-tech
    Contributor

    I checked the real source CLI on bcf23b6 with isolated XDG directories and a private server (no model calls). models --standalone --print-logs makes the diagnostic visible:

    • {: warning with the config path, path=$, and rejected malformed JSON or JSONC document.
    • {"model":42}: warning at $.model, skipped malformed recognized value.
    • {}: no configuration warning.

    Both warnings also reached $XDG_DATA_HOME/opencode/log/opencode-local.log. All three runs still exit 0 and list models, so this confirms a troubleshooting path, not a fix for the default silent UX. The existing source-aware/redaction and invalid-file controls also pass.

    The CLI's --print-logs help explicitly says server logs require --standalone; printing only the client logs can miss this loader warning. AI-assisted investigation.

  3. Velmet44 commented on Oct 12, 2026

    @Velmet44

    Reproduced on a source build of v2 (Windows): '{' as the entire global opencode.json -> models --standalone lists built-ins, exit 0, no error. Root cause looks like packages/core/src/config.ts: parseInfo logs a warning and returns undefined on malformed JSON, and loadFile then silently skips the file. I'd like to work on this - leaning toward failing startup with the file path and parse error rather than a log-only warning. Happy to do a surfaced warning instead if maintainers prefer.

  4. Velmet44 commented on Oct 12, 2026

    @Velmet44

    Follow-up from digging into the loader: the silent skip looks intentional and pinned. packages/core/src/config.ts parseInfo/loadFile logs a configuration normalization diagnostic and skips the file, and packages/core/test/config/config.test.ts:690-781 feeds malformed global, project, explicit-file, wellknown, and OPENCODE_CONFIG_CONTENT sources while asserting load succeeds. So failing hard on invalid files would flip that tested contract. Two options: (a) fail startup with the file path and parse error (updating that test), or (b) keep loading and add a user-visible warning path (none exists today; logs need --print-logs --standalone). Which do maintainers prefer?

  5. reynold-hu commented on Oct 12, 2026

    @reynold-hu
    Contributor

    I reached the same conclusion that the silent skip is pinned by packages/core/test/config/config.test.ts:690. I went with option (b): the loader now keeps a rejected file as a Config.Rejected entry in /api/config and the CLI reports it on stderr before running, without changing the graceful-load contract. PR: #54804

  6. Velmet44 commented on Oct 15, 2026

    @Velmet44

    reynold-hu's approach matches option (b) from my analysis above - standing down here. Happy to help review or test #54804 if useful.

  7. reynold-hu commented on Oct 16, 2026

    @reynold-hu
    Contributor

    Thank you! Yes, I went with your option (b) — the silent-skip contract pinned in config.test.ts stays intact, and rejected files now surface as Config.Rejected entries via /api/config plus a CLI stderr warning. A review would be very welcome, especially on the Rejected entry shape in the public API (packages/schema/src/config.ts) and the extra /api/config fetch in server-connection.ts. If you can re-run your Windows repro (malformed global config, models --standalone), that would be ideal — you should now see the warning on stderr instead of silence.

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