You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
service set/unset silently erases the stored config when service.json cannot be decoded #52412
Every mutating path writes back { ...read(), changed }, so a config that fails to decode is treated as an empty one and immediately overwritten with only the field being changed. The command reports success.
Steps to reproduce
Put a config file that cannot be decoded in place, e.g. one truncated mid-write:
The stored hostname, port, password and env are gone, with no warning. opencode service get prints {} for the same file, and starting the managed service reaches the same read through ServiceConfig.password, which then issues a new credential instead of the one its own comment says it keeps across restarts.
A torn file is reachable without hand-editing: ServiceConfig.write publishes through a fixed service.json + ".tmp" with no lock, so two overlapping service set runs can interleave on that one path before the atomic rename. config/config.ts wraps its own config write in Flock.acquire, and services/service-registration.ts uses a unique temp name.
This issue might be a duplicate of an existing issue. Please check:
#46128: reports silent data loss from unlocked whole-file JSON read-modify-write operations during concurrent writes, affecting auth.json rather than service.json.
Thanks for the detailed report. I can reproduce this.
Commit 5556ad563 isn't in a release, so I tested the latest release, @opencode/[email protected], using throwaway XDG directories:
Wrote a truncated service.json containing hostname, port, password and env.
opencode service get printed {}.
opencode service set env NEW_VAR v exited 0 with no warning.
The file now contained only {"env":{"NEW_VAR":"v"}}. Everything else was gone.
It still happens on the latest dev (f0160a1) run from source. There, ServiceConfig.read still returns {} when the file can be read but neither the current nor the legacy format decodes it, and the write paths then overwrite the file. On dev, the temp file name is now per-process, which covers the fixed service.json.tmp collision you described. I did not test the concurrency part.
The script below reproduces it.
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces anomalyco/opencode#52412: an undecodable service.json is silently replaced.# Usage: ./repro.sh (uses `opencode` from PATH)# OC_DEV=/path/to/checkout ./repro.sh (runs CLI from source)set -u
WORK=$(mktemp -d)export XDG_CONFIG_HOME=$WORK/config XDG_STATE_HOME=$WORK/state XDG_DATA_HOME=$WORK/data XDG_CACHE_HOME=$WORK/cache
mkdir -p "$XDG_CONFIG_HOME/opencode"oc() {
if [ -n"${OC_DEV:-}" ];then
bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js""$OC_DEV/packages/cli/src/index.ts""$@"else opencode "$@";fi
}
echo"version: $(oc --version)"# Builds run from source use the "local" channel, whose file is service-local.jsonif [ -n"${OC_DEV:-}" ];then CFG=$XDG_CONFIG_HOME/opencode/service-local.json;else CFG=$XDG_CONFIG_HOME/opencode/service.json;fi# A file truncated mid-write (fails JSON decoding)printf'{"hostname":"127.0.0.1","port":4321,"password":"user-secret","env":{"API_KEY":"sk-live-1'>"$CFG"echo"--- before:"; cat "$CFG";echoecho"--- opencode service get:"; oc service get
echo"--- opencode service set env NEW_VAR v:"; oc service set env NEW_VAR v;echo"exit=$?"echo"--- after:"; cat "$CFG"
Activity
github-actions commentedon Oct 1, 2026
This issue might be a duplicate of an existing issue. Please check:
auth.jsonrather thanservice.json.opencodejr commentedon Oct 28, 2026
Thanks for the detailed report. I can reproduce this.
Commit
5556ad563isn't in a release, so I tested the latest release,@opencode/[email protected], using throwaway XDG directories:service.jsoncontaining hostname, port, password and env.opencode service getprinted{}.opencode service set env NEW_VAR vexited 0 with no warning.{"env":{"NEW_VAR":"v"}}. Everything else was gone.It still happens on the latest
dev(f0160a1) run from source. There,ServiceConfig.readstill returns{}when the file can be read but neither the current nor the legacy format decodes it, and the write paths then overwrite the file. Ondev, the temp file name is now per-process, which covers the fixedservice.json.tmpcollision you described. I did not test the concurrency part.The script below reproduces it.
Reproduction script (repro.sh)
Run it:
bash repro.sh