Whole-config rejection without last-good fallback kills all running sessions when a concurrent writer leaves a transient partial state in opencode.jsonc

オープン
#62,872 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
48/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
json, typescript
領域
backend

調査の方向性

Start with packages/core/src/config.ts, especially parseInfo and loadFile, and compare the well-known source fallback policy described there. Then inspect packages/util/src/fs-util.ts and the atomic temp-file rename pattern in packages/core/src/git.ts. Define done as ensuring transient partial writes cannot make active sessions lose the last valid configuration; the issue describes atomic writes or last-good fallback as acceptable approaches.

索引モデルが issue の本文から書いたものです。

説明

reproduced
Description

When any concurrent writer leaves a transient partial state in the live global config file (~/.config/opencode/opencode.jsonc), a reloading opencode process fails to parse it and rejects the WHOLE configuration document - there is no fallback to the last-good configuration. The provider catalog then loses every model, and each running session dies at its next step with SessionRunnerModel.ModelUnavailableError ("Failed to drain Session"). Observed live on v2.2.5 with several concurrent opencode serve --stdio / ACP clients: 11 "rejected malformed JSON or JSONC document" normalization warnings, each followed within seconds by session-drain failures across three server processes.

Two contributing factors verified at tag v2.2.5:

  1. Whole-document rejection without last-good fallback: packages/core/src/config.ts (parseInfo/loadFile) drops the entry entirely when JSONC parsing fails. The codebase already follows the opposite policy for well-known sources - a failed refresh keeps the last resolved config "so remote providers and allowlists do not disappear" (config.ts, comment above the wellknown load path) - so extending that resilience to parse failures of the user's own config matches existing intent.
  2. In-place write of the live config: packages/core/src/config.ts line 482 persists via fs.writeWithDirs(filepath, ...), which resolves to a direct fs.writeFileString(path, content) without a temp-file + rename swap (packages/util/src/fs-util.ts, writeWithDirs). The codebase already uses an atomic temp+rename pattern elsewhere (for example packages/core/src/git.ts around line 967).

Reproduction notes: hammering PATCH /api/experimental/config 1200x against one server while another watched the same directory did NOT reproduce a partial read through opencode's own write path - its synchronous write completes before the watcher's next tick can observe intermediate bytes. However, any slower concurrent writer (editor autosave, scripts, plugin installs touching large configs) leaves the truncated-to-flushed window open long enough for readers to hit it, which matches how the live incidents occurred outside of controlled testing.

Expected behavior: readers of the live config never lose the entire configuration because of one transiently inconsistent state - either writes go through an atomic swap so partial states cannot be observed at all, or a parse failure falls back to the last-known-good document instead of dropping everything.

Plugins

none

OpenCode version

v2.2.5

Steps to reproduce
  1. Run two opencode server processes that watch the same global config directory.
  2. While sessions are active on both, have any third party rewrite the global config non-atomically and slowly enough that a reload lands inside the truncated-to-flushed window (editor-style save or chunked script write over a multi-kilobyte config).
  3. Observe "configuration normalization diagnostic ... rejected malformed JSON or JSONC document" in the log immediately followed by "Failed to drain Session" errors with ModelUnavailableError on all active sessions - no manual intervention recovers them until the file parses again.
Screenshot

n/a

Operating System

Ubuntu 24.04.5 LTS

Terminal

not applicable (ACP client)

Related Issues
  • https://github.com/anomalyco/opencode/issues/16450 - plugin config files deleted when multiple opencode instances run concurrently; different mechanism (concurrent bun install deleting files), closed without a fix. This report covers whole-document rejection on partial reads, which kills running sessions instead of losing files.
主要言語
TypeScript
スター
225k
フォーク
30.4k
平均マージ
4時間 16分
マージ済み PR(30日)
528

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

anomalyco/opencode のほかの issue

anomalyco/opencode の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。