Summary
bun run --cwd packages/plugin build fails at its final build:v2 step on clean upstream/master, so bun run build never exits 0:
$ cd /tmp/mc-upstream-cmp && git rev-parse --short HEAD
dc952bf3
$ bun run --cwd packages/plugin build:v2
ResolveMessage: Cannot find module '/tmp/mc-upstream-cmp/packages/plugin/server' from '/tmp/mc-upstream-cmp/packages/plugin'
at entry (…/@opencode/plugin/dist/host.js:24:27)
at resolve (…/@opencode/plugin/dist/host.js:29:22)
at <anonymous> (packages/plugin/src/v2/server.test.ts:50:30)
(fail) union entry satisfies both loaders without a ./server override
3 pass · 1 fail
error: script "build:v2" exited with code 1
Fresh /tmp worktree off master, real bun install, no fork code.
Note the build bundle is fine — build:v2 runs last, after dist/index.js is emitted, so the artifact exists and loads correctly. Only the exit code is red. Worth knowing if CI gates on it, and worth not mistaking a stale dist for a fresh one when the build "fails".
Cause
server.test.ts:50 calls Host.resolve(...), which probes optional entry subpaths ["server", ""] and is designed to swallow not-found and return undefined. The guard in @opencode/plugin@2.0.3 dist/host.js:14-24:
catch (error) {
if (!(error instanceof Error) ||
!("code" in error) ||
!["ENOENT","ENOTDIR","MODULE_NOT_FOUND","ERR_MODULE_NOT_FOUND",
"ERR_PACKAGE_PATH_NOT_EXPORTED","ERR_UNSUPPORTED_DIR_IMPORT"].includes(String(error.code)))
throw error;
}
Under bun, resolveModule is Bun.resolveSync (@opencode/util maps #runtime-import → ./dist/runtime/import.bun.js under the bun condition). Probing the absent subpath:
$ bun -e "try { Bun.resolveSync(dir + '/server', dir) } catch (e) { … }"
server threw: ResolveMessage code= "ERR_MODULE_NOT_FOUND" instanceof Error: false in swallow list: true
The code is correct and is in the list — ERR_MODULE_NOT_FOUND. The failure is the first clause: Bun's ResolveMessage is its own class and is not instanceof Error, so !(error instanceof Error) is true, the || short-circuits, and it re-throws before the code list is ever consulted.
I want to flag that I initially assumed the opposite — that bun was emitting a code outside the list — and that guess was wrong. The list is fine; the type check is what rejects it. Anyone reading the stack trace is likely to land on the same wrong hypothesis, so it seems worth stating explicitly.
Why this is arguably upstream's to route, not just a vendor bug
The defect lives in @opencode/plugin@2.0.3, but it is reachable only because a bun-run test drives a resolver designed around Node's error shapes. Options as I see them, without a strong opinion on which is right:
- Report upstream to
@opencode/plugin — the durable fix is duck-typing the error (typeof error === "object" && error && "code" in error) instead of instanceof Error, since bun's resolver is a first-class target for that package.
- Have
server.test.ts tolerate the bun shape, so the repo's own build is not gated on a third-party type check.
- Pin/patch the dependency until it lands upstream.
Environment
Clean upstream/master dc952bf3, dedicated /tmp worktree, bun install from lockfile, Bun 1.3.14, Linux x64, @opencode/plugin@2.0.3, @opencode/util@2.0.3. Reproduced identically on our fork tree, which does not touch src/v2/, package.json exports, index.js, or bun.lock — all four are byte-identical to upstream at that sha.
Summary
bun run --cwd packages/plugin buildfails at its finalbuild:v2step on cleanupstream/master, sobun run buildnever exits 0:Fresh
/tmpworktree off master, realbun install, no fork code.Note the build bundle is fine —
build:v2runs last, afterdist/index.jsis emitted, so the artifact exists and loads correctly. Only the exit code is red. Worth knowing if CI gates on it, and worth not mistaking a staledistfor a fresh one when the build "fails".Cause
server.test.ts:50callsHost.resolve(...), which probes optional entry subpaths["server", ""]and is designed to swallow not-found and returnundefined. The guard in@opencode/plugin@2.0.3dist/host.js:14-24:Under bun,
resolveModuleisBun.resolveSync(@opencode/utilmaps#runtime-import→./dist/runtime/import.bun.jsunder thebuncondition). Probing the absent subpath:The code is correct and is in the list —
ERR_MODULE_NOT_FOUND. The failure is the first clause: Bun'sResolveMessageis its own class and is notinstanceof Error, so!(error instanceof Error)istrue, the||short-circuits, and it re-throws before the code list is ever consulted.I want to flag that I initially assumed the opposite — that bun was emitting a code outside the list — and that guess was wrong. The list is fine; the type check is what rejects it. Anyone reading the stack trace is likely to land on the same wrong hypothesis, so it seems worth stating explicitly.
Why this is arguably upstream's to route, not just a vendor bug
The defect lives in
@opencode/plugin@2.0.3, but it is reachable only because a bun-run test drives a resolver designed around Node's error shapes. Options as I see them, without a strong opinion on which is right:@opencode/plugin— the durable fix is duck-typing the error (typeof error === "object" && error && "code" in error) instead ofinstanceof Error, since bun's resolver is a first-class target for that package.server.test.tstolerate the bun shape, so the repo's own build is not gated on a third-party type check.Environment
Clean
upstream/masterdc952bf3, dedicated/tmpworktree,bun installfrom lockfile, Bun 1.3.14, Linux x64,@opencode/plugin@2.0.3,@opencode/util@2.0.3. Reproduced identically on our fork tree, which does not touchsrc/v2/,package.jsonexports,index.js, orbun.lock— all four are byte-identical to upstream at that sha.