fix: stop opencode.json wipe and install panic on failure paths by gzb1128 · Pull Request #17 · gzb1128/opencode-plugin · GitHub
Skip to content

fix: stop opencode.json wipe and install panic on failure paths - #17

Merged
gzb1128 merged 1 commit into
mainfrom
fix/mcp-config-wipe-and-install-panic
Jun 17, 2026
Merged

fix: stop opencode.json wipe and install panic on failure paths#17
gzb1128 merged 1 commit into
mainfrom
fix/mcp-config-wipe-and-install-panic

Conversation

@gzb1128

@gzb1128 gzb1128 commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Summary

Closes 3 silent-failure / false-success bugs surfaced by a project quality review. All three are failure paths the test suite did not previously cover.

Problems

1. writeOpenCodeConfig wiped a corrupt opencode.json (Critical, data loss)
When the existing opencode.json was truncated/corrupt, json.Unmarshal was ignored, fullConfig stayed nil, and the file was rewritten with only the mcp key — destroying the user's model/provider/permissions/$schema config.

2. readOpenCodeConfig masked a malformed mcp block (Important)
A mcp value of the wrong JSON type (e.g. a string) was silently treated as empty, so every caller saw "no MCP servers" with no error.

3. Install panicked after a false "Successfully installed" (Critical)
installOneResolvedPlugin dereferenced the (*ComponentCounts) return without a nil-check; when symlink creation failed (e.g. ~/.agents not writable) it returned nil, err, the code printed ⚠️ Warning then ✓ Successfully installed, then nil-deref panicked. The Update path already handled this correctly — Install did not.

Fix

  • internal/mcp/manager.go: return parse errors (wrapped with %w) instead of wiping/masking. File-not-found path preserved.
  • internal/plugin/installer.go: nil-check counts before deref, mirroring the Update path.

Testing

Strict TDD — each test written RED-first, watched fail for the right reason, then watched pass:

Test RED (before) GREEN (after)
TestWriteOpenCodeConfig_CorruptFileIsPreservedNotWiped returned nil, would wipe file returns error, file byte-identical
TestReadOpenCodeConfig_CorruptMCPBlock_ReturnsError returned nil (silent) returns error
TestInstall_SymlinkFailureDoesNotPanic panic nil pointer dereference no panic

Gates: go build ✅ · go vet ✅ · gofmt -l clean ✅ · go test ./... all 11 packages pass incl. e2e ✅ · golangci-lint no new issues (the 2 prior json.Unmarshal errcheck items resolved).

Caller safety

readOpenCodeConfig / writeOpenCodeConfig are unexported. All 5 internal callers (InstallMCPConfig, UninstallMCPConfig, DisableMCPConfig, EnableMCPConfig, ListMCPServers) already propagate non-NotExists errors via return err — the new corrupt-config errors surface correctly; no behavior change on the happy path.

Out of scope (flagged in the review, not addressed here)

Non-atomic config writes, Update Stage-2 atomicity, Remove false-success, --force wiping real dirs, shallow clones, file modes (06440600). These are semantic/redesign changes worth a separate pass.

writeOpenCodeConfig silently overwrote a corrupt opencode.json with only
the mcp key, destroying the user's model/provider/permissions config.
readOpenCodeConfig masked a malformed mcp block as empty, and the Install
path panicked after printing a false 'Successfully installed' when symlink
creation failed (e.g. ~/.agents not writable).

All three are silent-failure / false-success paths the test suite did not
cover. Regression tests added (written RED-first, watched fail then pass).

- mcp: return parse errors instead of wiping or masking user config
- installer: nil-check *ComponentCounts before deref (mirrors Update path)
@gzb1128
gzb1128 merged commit 686e013 into main Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant