Skip to content

fix(tui): warn when CLI config is invalid - #56335

Open
1052326311 wants to merge 4 commits into
anomalyco:devfrom
1052326311:cli-config-warning
Open

1052326311 wants to merge 4 commits into
anomalyco:devfrom
1052326311:cli-config-warning

Conversation

@1052326311

@1052326311 1052326311 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #56318

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

An invalid field in cli.json makes the whole file fall back to defaults without telling the user. This keeps strict whole-file rejection and the existing fallback, but reports the file and failing field in the standard TUI and Mini.

The standard TUI warning now also follows live file edits. Repeated events with the same diagnostic do not repeat the warning; a valid reload clears it without dismissing an unrelated current notification. JSON parse errors report a line and column instead of an offset.

How did you verify your code works?

Verified at 0562f78cce57d4d9e64ea2c357182b07138b73f7 against dev@4d30794bcf4dccdcadbcb1f62bb35b4943a96426:

  • cd packages/cli && bun test --timeout 30000 test/config.test.ts: 20 passed.
  • cd packages/tui && bun test --timeout 30000 test/config-diagnostic.test.tsx test/config-v2.test.tsx test/mini/splash.test.ts: 57 passed; bun test --timeout 30000 test/client-mcp.test.tsx: 3 passed.
  • cd packages/core && bun run script/test.ts test/mcp.test.ts: 72 passed.
  • bun typecheck and bun run typecheck:tsc passed in CLI, TUI, and core. Root bun run check passed (34/34 tasks), including the pre-push hook. The native macOS arm64 CLI build passed with --single --skip-install --skip-web-ui; the PR diff passes git diff --check.
  • Prettier passes on 12 changed files. Two files still flag formatting on lines unchanged from dev.

Test-only follow-up at 49e2920: Removed the duplicate fs.watch polling test and split the rendered invalid-edit/valid-recovery case from the unrelated-toast preservation case. On this head, the two focused TUI files passed 25/25 and 250/250 across ten reruns; CLI config passed 20/20. TUI bun typecheck and typecheck:tsc, root bun run check (34/34), Prettier on both changed tests, and git diff --check passed. Production files are unchanged from 0562f78.

Screenshots / recordings

The before image is a baseline-equivalent control frame with the diagnostic omitted, not a native screenshot from the old commit. Both images capture the real TUI renderer at startup; live reload is covered by the rendered TUI test above.

Before control After
Invalid CLI config with no notice Invalid CLI config warning in the TUI

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #55670

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes the reported case. With an invalid top-level leader in cli.json, the base starts with defaults and shows nothing. With this PR, a warning toast names the file and the failing field (before/after screenshots below). The PR's config, TUI and Mini splash tests pass, and both packages type-check.

Public API: unchanged. The only addition is an optional diagnostic() on the internal CLI/TUI config interfaces. The cli.json format and its schema are not affected.

Concerns:

  • The warning only appears at startup. The TUI watches cli.json and reloads it when it changes. If you save an invalid edit while the TUI is running, every setting silently resets to the defaults and no warning appears. That is the same silent failure the issue reports, just on a path users hit often (see inline). The PR says a valid reload clears the diagnostic. That only updates the stored value; a toast that is already on screen stays for its full 15s.
  • JSON parse errors give a character offset, which is hard to find in an editor (inline).
  • Settings passed through OPENCODE_CLI_CONFIG_CONTENT are still dropped without any message. A minor gap, but the same problem.

Related: #55670

Before

After

Comment thread packages/tui/src/app.tsx Outdated
)
}

function ConfigDiagnosticToast(props: { diagnostic?: string }) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diagnostic is read once at startup, and this shows it once on mount. ConfigProvider (packages/tui/src/config/index.tsx) re-runs host.get() whenever cli.json changes. So if you introduce an invalid field while the TUI is running, apply({}) resets every setting to the defaults with no warning, which is the silent failure from #56318 again. Could the watcher's reload path read host.diagnostic?.() after each get() and show the toast when it changes (and possibly dismiss it once the file is valid again)? Then startup and live edits would both go through one place.

Comment thread packages/cli/src/config/config.ts Outdated
const error = errors[0]
if (error) {
const field = getLocation(text, error.offset).path.join(".") || "root"
return { reason: `${field}: ${printParseErrorCode(error.error)} at offset ${error.offset}` }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at offset 123 is hard to act on in an editor. Converting the offset to a line:column (counting newlines in text.slice(0, error.offset)) would make parse errors much easier to fix.

@1052326311

Copy link
Copy Markdown
Contributor Author

Addressed both points from review 5556764944 in 6ac7c7c0f727f6b2943a132e120e30e1388c4040:

  • ConfigProvider now publishes the CLI diagnostic after each watched reload. The rendered TUI test covers an invalid live edit, repeated identical events, recovery after a valid edit, and preservation of an unrelated current toast. apply(info) remains unconditional, so invalid files still use the existing empty-config fallback; this does not adopt retain-last-good behavior.
  • JSON parse errors now identify the field and line:column instead of an offset.

Local verification: packages/cli: bun test --timeout 30000 test/config.test.ts (20/20); packages/tui: bun test --timeout 30000 test/config-diagnostic.test.tsx test/config-v2.test.tsx test/mini/splash.test.ts (57/57), plus 10 repeats of the two config files (250/250). Both package bun typecheck commands, root bun run check (35/35 typecheck tasks), and the native arm64 CLI build (--single --skip-install --skip-web-ui) passed. The fork check, test, and nix-eval workflows are currently action_required with zero jobs, so their product CI has not run.

@1052326311

Copy link
Copy Markdown
Contributor Author

Merged dev@4d30794bcf4dccdcadbcb1f62bb35b4943a96426 at 0562f78cce57d4d9e64ea2c357182b07138b73f7. The app.tsx conflict keeps both the CLI config warning toast and upstream ClientMcp; the PR delta remains 14 CLI/TUI files with no core changes.

Fresh local checks on this head: CLI config 20/20, TUI/Mini 57/57, TUI client MCP 3/3, core MCP 72/72; CLI/TUI/core bun typecheck and typecheck:tsc, root bun run check 34/34, and native macOS arm64 CLI build passed.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both earlier points are fixed. I edited cli.json while the TUI was running. Adding an invalid field shows the warning naming leader, broken JSON reports a line and column (at 3:1), and fixing the file clears the toast. On the base, none of these edits shows anything. The CLI config tests and the TUI diagnostic, config and Mini splash tests pass, and both packages type-check.

Public API: unchanged. The only addition is an optional diagnostic on the internal CLI/TUI config interfaces.

The only new concern is the size and timing of the added live-reload tests (inline).

  • packages/tui/test/config-v2.test.tsx:331: This test waits on real fs.watch events in Bun.sleep(10) loops with 2s deadlines. On a slow or loaded CI machine the watcher can take longer, so it may fail at random. It also expects "mouse on" after the invalid reload only because {} falls back to the default, which is hard to follow. The live-reload behaviour is already covered in config-diagnostic.test.tsx. Could you drop this one, or reduce it to a single invalid-then-valid reload driven by the fixture rather than wall-clock polling?
  • packages/tui/test/config-diagnostic.test.tsx:22: This test goes through a long chain of states (invalid, the same invalid file again, valid, invalid, then valid while another toast is showing) using read and check counters. That makes it hard to follow and adds a lot of timing-dependent steps. Please cut it down to the core case: an invalid edit shows the warning and a valid edit clears it. The other-toast case could be a small separate test if you want to keep it.

Before

After

@1052326311

Copy link
Copy Markdown
Contributor Author

Addressed review 5624771262 in 49e2920f603dede7061cda6c7117700687739920: removed the duplicate fs.watch/Bun.sleep(10) test and split the rendered invalid-edit/valid-recovery check from the unrelated-toast check. Fresh local results on this head: focused TUI 25/25 and 250/250 across ten reruns; CLI config 20/20; TUI Bun and native TypeScript typechecks, root check 34/34, Prettier, and diff check passed. Production code is unchanged.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new commits only change tests and address the earlier test feedback. The polling test in config-v2.test.tsx is gone. The live-reload test in config-diagnostic.test.tsx is now two small tests that wait for rendered frames. They passed on each of three runs and packages/tui type-checks. Production code is unchanged since my last review, where I confirmed that live invalid edits show the warning and valid edits clear it.

Public API: unchanged. The only addition is an optional diagnostic on the internal CLI/TUI config interfaces.

No further concerns.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tui: one invalid cli.json field silently discards the entire config

1 participant