fix: Config.update() writes to wrong file, PATCH /config settings silently lost#505
fix: Config.update() writes to wrong file, PATCH /config settings silently lost#505saravmajestic wants to merge 4 commits intomainfrom
Conversation
…fig` are silently lost
`Config.update()` wrote to `config.json` in the project directory, but
`Config.state()` loads project config from `opencode.json` / `opencode.jsonc`
via `ConfigPaths.projectFiles("opencode", ...)`. The file names didn't match,
so any setting saved through `PATCH /config` (model selection, provider config,
etc.) was written to a file that was never read back.
This caused the VS Code extension's model picker to appear to save the
selection but the server would fall back to the provider-level default model
on the next request — leading to errors like "No endpoints found for
google/gemini-3-pro-preview" when the default model was stale.
The fix finds the existing project config file using the same
`ConfigPaths.projectFiles()` that `state()` uses, falling back to
`opencode.json` if none exists.
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
💤 Files with no reviewable changes (1)
📝 WalkthroughWalkthroughThe Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/opencode/src/config/config.ts (1)
1431-1434: Preserve.jsonccontent when updating project config.If
filepathresolves toopencode.jsonc, Line 1434 rewrites withwriteJson, which strips comments/JSONC formatting. Consider patching JSONC in-place (same approach asupdateGlobal) to avoid destructive edits.Suggested diff
const projectFiles = await ConfigPaths.projectFiles("opencode", Instance.directory, Instance.worktree) const filepath = projectFiles[projectFiles.length - 1] ?? path.join(Instance.directory, "opencode.json") - const existing = await loadFile(filepath) - await Filesystem.writeJson(filepath, mergeDeep(existing, config)) + if (filepath.endsWith(".jsonc")) { + const before = await Filesystem.readText(filepath).catch(() => "{}") + const updated = patchJsonc(before, config) + parseConfig(updated, filepath) + await Filesystem.write(filepath, updated) + } else { + const existing = await loadFile(filepath) + await Filesystem.writeJson(filepath, mergeDeep(existing, config)) + } await Instance.dispose()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/src/config/config.ts` around lines 1431 - 1434, The current write path always uses Filesystem.writeJson which will strip comments when filepath is a .jsonc; change the update to preserve JSONC by detecting when filepath endsWith(".jsonc") and applying the same in-place JSONC patching logic used by updateGlobal instead of Filesystem.writeJson — use the existing loadFile/mergeDeep result to compute the patch and call the JSONC patch/update helper (the same routine updateGlobal uses) for .jsonc files, falling back to Filesystem.writeJson for plain .json files; keep references to ConfigPaths.projectFiles, loadFile, mergeDeep and Filesystem.writeJson when locating the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/opencode/src/config/config.ts`:
- Around line 1431-1434: The current write path always uses Filesystem.writeJson
which will strip comments when filepath is a .jsonc; change the update to
preserve JSONC by detecting when filepath endsWith(".jsonc") and applying the
same in-place JSONC patching logic used by updateGlobal instead of
Filesystem.writeJson — use the existing loadFile/mergeDeep result to compute the
patch and call the JSONC patch/update helper (the same routine updateGlobal
uses) for .jsonc files, falling back to Filesystem.writeJson for plain .json
files; keep references to ConfigPaths.projectFiles, loadFile, mergeDeep and
Filesystem.writeJson when locating the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 973d44b2-5d00-4cc2-88cd-25c7a549bd23
📒 Files selected for processing (1)
packages/opencode/src/config/config.ts
`Config.update()` now writes to `opencode.json` (via `ConfigPaths.projectFiles()`), not the legacy `config.json`. Update the assertion to match. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… provides declarations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 29254673 | Triggered | Generic Password | cb95365 | packages/opencode/test/altimate/connections.test.ts | View secret |
| 29254674 | Triggered | Generic Password | cb95365 | packages/opencode/test/altimate/connections.test.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
Summary
Config.update()wrote toconfig.jsonin the project directory, butConfig.state()loads project config fromopencode.json/opencode.jsoncviaConfigPaths.projectFiles("opencode", ...). The file names didn't match, so any setting saved throughPATCH /configwas written to a file that was never read back.No endpoints found for google/gemini-3-pro-previewwhen the default model was stale/removed from OpenRouter.ConfigPaths.projectFiles()thatstate()uses, falling back toopencode.jsonif none exists.Root Cause
state()?Config.update()(before fix)config.jsonGlobal.Path.config, never from project dirConfig.state()project loadingopencode.json/opencode.jsoncConfig.update()(after fix)ConfigPaths.projectFiles()or falls back toopencode.jsonHow it was found
APIError:with no message when sending messagesNo endpoints found for google/gemini-3-pro-preview(a stale default model)PATCH /configwith{"model": "openrouter/anthropic/claude-sonnet-4.6"}→ returned successGET /configright after didn't include themodelfield — it was lostConfig.update()writingconfig.jsonwhileConfig.state()readsopencode.jsonTest plan
PATCH /configwith{"model": "openrouter/anthropic/claude-sonnet-4.6"}GET /config— verifymodelfield is present in responseopencode.json/opencode.jsoncfiles are updated in-place (not a new file created)opencode.json🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests
Chores