fix(core): break filesystem cycle in compiled prompts - #48397
kernel-oops wants to merge 1 commit into
Conversation
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
Independent confirmation of this fix on macOS arm64, which I think is the platform gap left by the verification above (Linux x64/glibc). I hit #48372 after a routine Environment
The undefined dependency is directly observableRather than driving a full prompt, the bad dependency can be observed at the graph level. Forcing the hazardous evaluation order (search before filesystem) and bundling is enough: import "./src/filesystem/search"
import { FileSystem } from "./src/filesystem"
const deps = (FileSystem.node as any).dependencies
console.log(deps.map((d: any) => d?.name ?? "<UNDEFINED>"))Before (unpatched With this PR's That { cache, resolve: (node) => replacementMap.get(node.name) ?? node }
Note this only manifests bundled. Run from source, the same cycle throws the much clearer Also checked, with this PR applied
This appears to be the only instance of the patternSince the failure class is "a Across 669 modules under Possible lighter-weight regression testThe bundle probe above needs no provider, credentials, network, or session, and runs in about a second. It may be worth considering alongside (or instead of) the full compiled runner, since it asserts the actual invariant — no A related thought for a separate change: ImpactWorth noting this is a full outage on the affected build — every prompt fails before any provider request, and the surfaced error is a generic |
|
Thanks @vidit19sharma — this independent reproduction and before/after validation materially close the macOS arm64 validation gap. The direct graph observation is particularly useful: it matches the compiled-dependency invariant this fix protects — every dependency entry must be a defined node. The source/bundled difference also explains the misleading symptom: source execution catches the premature access with a temporal-dead-zone The lighter bundle probe is a strong candidate to complement the full compiled runner: it checks the graph invariant directly and cheaply, while the runner retains end-to-end coverage of the compiled prompt/tool path. Thanks also for your scan across 669 modules finding only this hazard and none after the patch; that is useful additional evidence, though I have not independently reproduced the scan. Agreed that dependency validation in |
https://build.opensuse.org/request/show/1377875 by user pluskalm + anag_factory opencode 1.18.30: two fixes, supersedes 1377865 (same patch, one more change). - Every prompt failed with "undefined is not an object (evaluating 'a.name')" (boo#1280159): circular import between core/src/filesystem.ts and filesystem/search.ts, which bun 1.4.2's bundler orders so a layer dependency is undefined. Backport of upstream PR anomalyco/opencode#48397 (search.ts only). Reproduced on the Factory:ARM RPM in a Tumbleweed container; the patched build answers the same prompt. - NO_BRP_STRIP_DEBUG=true in %install: brp-15-strip-debug runs binutils strip (not %__strip) on any ELF `file` calls "not stripped", which the compiled binary is whenever the bun it was copied from kept its .symtab (any project without the debuginfo flag); that drops the appended payload and %check prints bun's ver
nixpkgs bumped bun to 1.4.2, and opencode 1.18.30 bundled with it fails every prompt with "Cannot read properties of undefined (reading 'name')" from its layer resolver. Building it with bun 1.3.13 restores a working binary until the upstream fix (anomalyco/opencode#48397) lands. Assisted-by: Claude Code (claude-opus-5)
|
Additional validation on Linux x86_64 / glibc 2.39 with Bun 1.4.1 ( Source baseline: OpenCode v1.18.31, commit Both before/after builds used Bun 1.4.1 and the standard native build with minification, splitting and embedded web UI: bun install --frozen-lockfile
OPENCODE_VERSION=1.18.31+patched.2 OPENCODE_CHANNEL=latest \
bun run packages/opencode/script/build.ts --single --skip-installThe unpatched build was labelled Before/after prompt check Using isolated working/config/data/cache directories and a deterministic local OpenAI-compatible SSE endpoint:
The failing full Omnigent run logged I also reproduced the dependency-graph probe described above with Bun 1.4.1: importing search before filesystem and bundling captured Checks after applying the fix
This adds Bun 1.4.1 and a real-model Omnigent integration check to the reported validation. It is a bounded smoke/regression check, not a long-running soak test or coverage of other platforms. |
- Cite the local reproduction of the 1.4.1+ splitting crash rather than oven-sh/bun#42837's own matrix, whose PASS criterion is an /api/agent 200 -- the same smoke test this comment says returns 200 on a broken binary. Add anomalyco/opencode#48397, which traces the crash to a filesystem import cycle and is where the fix may actually land. - Say what staying on 1.4.0 costs (the 1.4.1 fetch/dns fixes forgone), and why patching build.ts to `splitting: false` to reach 1.4.2 is worse than waiting: it puts us on a bundle shape nobody upstream runs. - Scope the re-bump rule to every platform a deployed host consumes, not just linux, and record that darwin has no agent-turn coverage today. - Narrow the new tripwire to the one test that exercises wrapSSE (the five headerTimeout tests return early before that path, and one races a 500 ms margin on a cold runner), record that it was proved non-vacuous on CI, and say plainly that the aisdk.ts wrapper is patched but not tripwired. - apply.sh: the sunset is 'when upstream already contains #44944', at which point dropping the patch is mandatory. The previous wording read as an absolute ban while the pin is >= 1.4.0.
…ibopentui.so leak (#54) * ci: build releases with bun 1.4.2 to stop the /tmp libopentui.so leak `bun build --compile` bakes the bun runtime, and therefore its embedded-file extractor, into the shipped binary, so the bun version this workflow pins is what every launch of the released binary runs. @opentui/core's platform package resolves its native library through `import("./libopentui.so", { with: { type: "file" } })`, which makes the 13.35 MB shared object an embedded file that bun must materialize on disk before the TUI can dlopen it. bun < 1.4.0 wrote a fresh `$TMPDIR/.<16 hex>-<8 digit>.so` on every launch and never unlinked it, not even on clean exit. Measured on a shared box on 2026-09-21: 1626 orphaned copies totalling 19.84 GB, accruing 1.1-2.1 GB/day across ~15 concurrent attach TUIs. That is oven-sh/bun#40076 (closed 2026-08-22; its report names opencode's libopentui.so as the real-world case), a duplicate of #29585, fixed by #29587 in bun 1.4.0: an embedded file now extracts once to a content-hashed, reused `$TMPDIR/.bun-{uid}-{hash}.{ext}`. Nothing above bun can fix this. The extraction is not opencode's code and not @OpenTui's; @opentui/core 0.4.5 is what upstream opencode dev still pins as of 2026-09-21, and upstream opencode still declares `packageManager: bun@1.3.14`, so this pin deliberately no longer matches upstream's. The prior rule ("match upstream's packageManager") is replaced by the same gate it always implied: a bun bump ships only after a `dry_run: true` run of this workflow on the bumped branch is green. Verified locally on aarch64 with the #40076 repro (a 69 KB lib.so imported `with { type: "file" }` and dlopen'd, compiled and run 3x): bun 1.3.3 left three files, one per launch, in /tmp and ignored $TMPDIR; bun 1.4.2 left one reused `$TMPDIR/.bun-1000-3a39a40e08be468e.so`. Refs: workstation-o5s1.24, workstation-o5s1.27 * fix: pin bun 1.4.0, not 1.4.2, and carry the SSE cancel-rejection backport Two corrections to the previous commit, both from evidence that the bump alone would have shipped broken binaries. 1. 1.4.2 -> 1.4.0. bun 1.4.1 rewrote `--splitting`, and packages/opencode/script/build.ts compiles with `minify: true, splitting: true`. A 1.4.1- or 1.4.2-compiled opencode binary crashes on the first agent turn with `TypeError: undefined is not an object (evaluating 'node.name')` in packages/core/src/effect/layer-node.ts -- reported against stock upstream `dev` on darwin-arm64 in anomalyco/opencode#44946 (the upstream bun-bump PR, still open for this reason) and tracked as oven-sh/bun#42837, where a 1.4.0-compiled binary passes and a 1.4.1 one fails. 1.4.0 carries the embedded-file fix (#29587) and predates the splitting rewrite. Verified locally that a 1.4.0-compiled binary reuses one `$TMPDIR/.bun-{uid}-{hash}.so` across launches, same as 1.4.2. That regression is invisible to a `--version` smoke test and to the named test steps here, so the comment's "dry_run green is enough" rule went with it: a bun bump now also requires executing the dry run's linux artifact for one real agent turn. 2. New patches/sse-cancel-rejection.patch, a backport of upstream #44944 (merged 2026-09-02, after v1.18.18, so not on the line the cron hold keeps us on). Under bun 1.4, `reader.cancel()` on a fetch body that was just aborted rejects; both SSE chunk-timeout wrappers discard that promise with `void`, which is an unhandled rejection, which bun answers by killing the process. chunkTimeout is armed on four providers in the deployed config, so one stalled stream would take a serve and every session it owns down with it. The two hunks are byte-identical to upstream's; upstream's third hunk is a test-only flake fix and is deliberately not carried. build-release now names the upstream test that goes red without the patch under bun 1.4 (test/provider/header-timeout.test.ts, per #44943), so neither half of this change can regress unnoticed. Refs: workstation-o5s1.24, workstation-o5s1.27 * docs: sharpen the bun-pin and tripwire comments after review - Cite the local reproduction of the 1.4.1+ splitting crash rather than oven-sh/bun#42837's own matrix, whose PASS criterion is an /api/agent 200 -- the same smoke test this comment says returns 200 on a broken binary. Add anomalyco/opencode#48397, which traces the crash to a filesystem import cycle and is where the fix may actually land. - Say what staying on 1.4.0 costs (the 1.4.1 fetch/dns fixes forgone), and why patching build.ts to `splitting: false` to reach 1.4.2 is worse than waiting: it puts us on a bundle shape nobody upstream runs. - Scope the re-bump rule to every platform a deployed host consumes, not just linux, and record that darwin has no agent-turn coverage today. - Narrow the new tripwire to the one test that exercises wrapSSE (the five headerTimeout tests return early before that path, and one races a 500 ms margin on a cold runner), record that it was proved non-vacuous on CI, and say plainly that the aisdk.ts wrapper is patched but not tripwired. - apply.sh: the sunset is 'when upstream already contains #44944', at which point dropping the patch is mandatory. The previous wording read as an absolute ban while the pin is >= 1.4.0.
Issue for this PR
Fixes #48372. Related to #44946; this complements that Bun upgrade rather than duplicating its runtime pin change. The upgrade is useful for fixes such as oven-sh/bun#35356 (GC timer CPU usage), but compiled prompt preparation also needs checking.
Type of change
What does this PR do?
Remove the runtime import from filesystem search back to the filesystem service. Schema constructors now come directly from the schema package; service input imports remain type-only.
The service depends on the search node. With Bun 1.4.2, the compiled cycle can capture an undefined dependency: health passes, but the first prompt fails in
SystemPrompt.environmentwithTypeError: undefined is not an object (evaluating 'a.name'), before any provider request.Add an isolated compiled regression and a path-filtered Linux CI job using Bun 1.4.2. The runner exercises authenticated session creation, a streamed request for the actual
readtool, completion, persisted tool output and a second turn. All provider responses are deterministic and local.How did you verify your code works?
Fresh upstream
devat193de13a88d62a6409c6d385831180f1def527dc, Linux x64/glibc, Bun 1.4.2, native minified/split builds with embedded web UI:Soak update: the operator reports a few days of normal server and TUI use with excellent CPU usage and no noticed issues. This is the custom
1.18.30-kernel-oops-bun142-promptfix1build with Bun 1.4.2, now used for normal projects, not pristine upstream or a controlled multi-day test. The clean-upstream regression results above are separate.Windows runner support, other binary targets, real providers and CPU benchmarks were not tested by this PR's isolated regression. Broader platform coverage and upstream review remain outstanding.
Screenshots / recordings
Not applicable.
Checklist