anomalyco / anomalyco/opencode
V1 1.18.31: every `opencode <script>.js` / plugin-hook subprocess leaks a fresh 13.7 MB libopentui.so into $TMPDIR (17 GB in <2 h)
@rekram1-node is already working on this.
Since Sep 16, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
On Linux x64 with opencode 1.18.31 (stable, Homebrew), every invocation of the compiled CLI that executes a JS script — e.g. plugin hook subprocesses spawned as opencode /path/hook.js — extracts a fresh 13,745,312-byte libopentui.so into $TMPDIR (/tmp) and never reuses or removes it. The embedded Bun runtime is v1.3.14, which predates the extraction-dedup fix (oven-sh/bun#29587, shipped in Bun 1.4.0).
In an agent setup where a plugin adapter spawns guard hooks with spawnSync(process.execPath, [hookPath]) on every tool/message event, this leaks ~13.7 MB per event. Observed accumulation: 1,332 files / 18,308,755,584 bytes (~17.05 GiB) in under 2 hours on a 32 GiB tmpfs /tmp, still growing (~12 files / 20 s during active tool use).
This is related to but distinct from #42700 / #28089 / #42880: this report pins the per-script-invocation / hook-subprocess amplification on current stable V1 and gives a deterministic, seconds-long repro that does not require a full TUI launch or a long-running server session.
Environment
- opencode 1.18.31, Homebrew (linuxbrew):
/home/linuxbrew/.linuxbrew/Cellar/opencode/1.18.31/bin/opencode, 185,030,784 bytes, sha256f9dab32248695e9e…, built 2026-09-14 - Embedded runtime:
Bun v1.3.14 (0d9b296a)(confirmed via strings in the binary) - Embedded native lib asset:
libopentui-h3hyjpa5.so - OS: Linux x86_64,
/tmpis tmpfs (32 GiB), uid/gid 1000 - Process topology: OpenChamber (
@openchamber/web1.23.2) →opencode serve(PID 1076) → per-eventopencode ~/.config/opencode/hooks/*.jschildren TMPDIR/BUN_TMPDIRunset for the server
Steps to reproduce (minimal, no TUI, no server)
D=$(mktemp -d /tmp/opencode-leak-repro.XXXXXX)
printf 'console.log("probe")\n' > /tmp/opencode-leak-probe.js
for i in 1 2 3; do
TMPDIR="$D" BUN_TMPDIR="$D" timeout 30 \
./opencode /tmp/opencode-leak-probe.js </dev/null >/dev/null 2>&1
done
find "$D" -maxdepth 1 -type f -printf '%s %f\n'
Result (verified):
before: 0 files
after: 3 files, 41,235,936 bytes
13745312 .9adffbfbfae1ffbf-00000000.so
13745312 .9adffbfbf6e4eddf-00000000.so
13745312 .9adffbfbf3e1ffdf-00000000.so
Each file: sha256 ce73133a58d35e35610ef53353ddeeeb93fb29505dde0cf1854ce25facee241d, SONAME libopentui.so (x86-64 ELF, not stripped, debug info, Zig/clang 20.1.2 via zig-bootstrap).
Control: opencode --version with the same TMPDIR/BUN_TMPDIR leaves 0 files, so this is specific to the script/hook execution path, not all CLI commands.
Observed in normal use (hook amplification)
- The plugin adapter runs each guard hook as a child process of the opencode binary:
spawnSync(process.execPath, [hookPath])(~/.config/opencode/plugins/gsd-core.js:230). - Sampling
/proc/<serve-pid>/task/<tid>/children+/proc/<pid>/mapsfor 20 s caught 23 short-livedopencode .../hooks/*.jschildren, each mapping a distinct, freshly-created 13,745,312-byte/tmp/.*-00000000.so. lsofon older copies shows none held open; files persist after the creating processes exit.- Natural accumulation: 1,033 → 1,332 files in ~45 min of inspection (rate scales with tool-call/message events); baseline idle ~1 file / 2 s.
- All sampled copies are byte-identical to each other and to the hash above.
Expected behavior
Each process reuses a single extracted copy (e.g. a content-hashed path) or cleans up after itself. $TMPDIR must not grow without bound from process invocations.
Actual behavior
One new randomly-named 13.7 MB copy per opencode <script>.js invocation; no dedup, no unlink on clean exit.
Impact
/tmpon tmpfs consumes RAM: ~17 GiB already; ENOSPC is reachable within hours of agent use.- At ENOSPC, upstream reports opencode failing with a misleading
Failed to open library "/$bunfs/root/libopentui-*.so"error (see #42700). - Hidden dotfiles can resemble malware and trigger incident response.
- Any workflow that shells out to
opencode <script>(plugin hooks, agent orchestrators, watchers) leaks proportionally to its event rate.
Root cause / attribution
- Bun 1.3.14 (root cause): extraction for
bun:ffidlopen()of embedded native libs in compiled binaries names each copy viatmpname(nanoTimestamp + per-VM counter)and never reuses/unlinks it. Fixed by oven-sh/bun#29587 (content-hashed.bun-{uid}-{hash}.{ext}reuse, shipped in Bun 1.4.0); tracked in oven-sh/bun#29585, #30962, #40076. - OpenCode V1 (amplifier): pins/embeds Bun 1.3.14 (
packageManagerin the rootpackage.json). The upgrade is already tracked: #44945 (open) and PR #44946 (open). This report is direct evidence that the V1 pin is the remaining source of the leak on stable. - OpenTUI (trigger):
@opentui/coreeagerly resolves anddlopen()s its embeddedtype: "file"native lib at module init, so merely importing the TUI code materializes the library — in a--compilebinary that materialization goes through the buggy Bun path above.
Workaround
- Contain extraction off tmpfs and purge periodically:
BUN_TMPDIR="$HOME/.cache/opencode-bun-tmp" TMPDIR="$HOME/.cache/opencode-bun-tmp" opencode ... - Age-based cleanup (safe while running on Linux; mapped inodes survive unlink). Filter by size so the unrelated 5,576,816-byte
.bcddef7ff5a7b7ee-00000000.so(a different embedded lib, held open byopencode serve) is not touched:find /tmp -maxdepth 1 -type f -regextype posix-extended \ -regex '.*/\.[0-9a-f]{16}-00000000\.so' -size 13745312c -mmin +60 -delete - Permanent: land #44946 (Bun ≥ 1.4.x). Once a fixed build is in use, the remaining stale files can be deleted after all older processes stop.
Related issues
- opencode: #42700 (open, assigned), #28089 (open), #42880 (open), #21427, #20043, #16996, #23804, #37671
- Bun: #29585, #30962, #40076; fix PR #29587 (shipped in Bun 1.4.0)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.