Skip to content

QVAC-18515 fix[api]: catch bare-runtime spawn failure instead of crashing the host#2467

Open
simon-iribarren wants to merge 5 commits into
tetherto:mainfrom
simon-iribarren:fix/qvac-18515-bare-runtime-binary-resolution
Open

QVAC-18515 fix[api]: catch bare-runtime spawn failure instead of crashing the host#2467
simon-iribarren wants to merge 5 commits into
tetherto:mainfrom
simon-iribarren:fix/qvac-18515-bare-runtime-binary-resolution

Conversation

@simon-iribarren

@simon-iribarren simon-iribarren commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🎯 What problem does this PR solve?

The install failure originally reported in #1492 (No binaries found for target '<platform>-<arch>' under pnpm) is already fixed on the current toolchain — it was a pnpm bug fixed in pnpm 11.0.0 (see "Original issue: already fixed" below). What remains unaddressed is the failure mode: when the Bare runtime binary genuinely can't be spawned, the SDK crashes the host process instead of throwing a catchable error.

bare-runtime resolves its platform binary synchronously with require('bare-runtime-<platform>-<arch>'). When that package — or one of its nested deps — can't be loaded, the spawn call throws synchronously inside the IPC listen callback. Nothing catches it, so it escapes as an uncaught exception and kills the host process. The thrown message is also misleading: a missing nested dependency surfaces as the terse "No binaries found".

📝 How does it solve it?

  • Wrap the synchronous spawn("bare", …) in client/rpc/node-rpc-client.ts in a try/catch. On failure it tears down the half-initialized RPC state, closes the IPC server, and rejects the init promise instead of letting the throw crash the process.
  • Map the raw No binaries found throw to a structured, exported BareRuntimeBinaryNotFoundError that names the platform/arch and tells the user how to recover (install bare-runtime-<platform>-<arch> directly, or install with npm/bun).
  • Document the same remedy in the CLI README.

This is platform-independent: any synchronous spawn failure now becomes a catchable error rather than a host crash.

✅ Original issue: already fixed

The #1492 install failure does not reproduce on the current toolchain — it was a pnpm resolution bug fixed in pnpm 11.0.0, not a bare-runtime/@qvac issue.

The bare-runtime platform package (e.g. bare-runtime-darwin-arm64) depends on require-asset, and older pnpm doesn't link require-asset into the virtual store (node_modules/.pnpm/node_modules/require-asset), so require('bare-runtime-<platform>-<arch>') fails and surfaces as the misleading "No binaries found". Reproduced on macOS (arm64) installing the originally-reported bare-runtime@1.28.4 and resolving the binary the way the SDK does:

pnpm result
8.15.9 / 9.15.4 / 10.17.1 No binaries found for target '<platform>-<arch>'
11.0.0+ ✅ resolves the bare binary

The bare-runtime version isn't the factor (even 1.28.4 resolves fine on pnpm 11). An earlier revision of this PR declared the bare-runtime-<platform>-<arch> packages as optionalDependencies of @qvac/sdk / @qvac/cli; since current pnpm already resolves the binary, those added no guarantee and were dropped to keep the change minimal.

🧪 How was it tested?

  • bun install + tsc --noEmit, eslint --max-warnings=0, and prettier --check on the changed files — all pass.
  • Reproduced the load failure locally (a missing nested dependency of the platform package surfaces as the misleading No binaries found) and confirmed the new path rejects with BareRuntimeBinaryNotFoundError instead of crashing the process.
  • pnpm version sweep on darwin-arm64 installing bare-runtime@1.28.4: fails on pnpm 8/9/10, resolves on pnpm 11 (see table above) — confirming the install issue is a pnpm bug already fixed upstream, hence the dropped optionalDependencies.
  • Note: end-to-end win32 + pnpm validation requires a Windows host and was not run here; the crash-handling fix is platform-independent.

🔌 API Changes

New exported error class BareRuntimeBinaryNotFoundError (non-breaking addition). Thrown during worker init when the platform's Bare runtime binary cannot be loaded.

import { BareRuntimeBinaryNotFoundError, completion } from "@qvac/sdk";

try {
  await completion({ /* ... */ });
} catch (err) {
  if (err instanceof BareRuntimeBinaryNotFoundError) {
    // platform binary missing — install `bare-runtime-<platform>-<arch>`
    // directly (pnpm), or install @qvac/sdk with npm or bun
  }
}

Closes #1492

@simon-iribarren simon-iribarren requested review from a team as code owners June 5, 2026 12:54
@simon-iribarren simon-iribarren added tier1 verified Authorize secrets / label-gate in PR workflows labels Jun 5, 2026
@simon-iribarren simon-iribarren changed the title QVAC-18515 fix[api]: resolve bare-runtime platform binary under pnpm QVAC-18515 fix[api]: catch bare-runtime spawn failure instead of crashing the host Jun 5, 2026
Comment thread packages/sdk/client/rpc/node-rpc-client.ts
The Node worker spawns via `bare-runtime`, which loads its platform binary
with `require('bare-runtime-<platform>-<arch>')`. As a transitive optional
dependency this package (or its nested deps) is frequently not installed under
pnpm even on a supported host, so `spawn` throws a terse
`No binaries found for target '<platform>-<arch>'` that escapes the IPC
listen callback as an uncaught exception and crashes the host process.

- Declare the desktop `bare-runtime-<platform>-<arch>` packages as
  optionalDependencies of `@qvac/sdk` and `@qvac/cli` so package managers
  install and link the matching platform binary directly (mirrors the
  esbuild/swc pattern).
- Catch the spawn failure in the Node RPC client and reject with a structured
  `BareRuntimeBinaryNotFoundError` (exported for `instanceof`) that names the
  platform and the remedy, instead of crashing with the raw message.
- Document the pnpm caveat and workaround in the CLI README.
Current pnpm installs and links bare-runtime's matching
bare-runtime-<platform>-<arch> package (and its nested deps) on its
own, even when bare-runtime is a transitive optional dependency, so
declaring the platform packages directly on @qvac/sdk and @qvac/cli
adds no resolution guarantee. The substantive fix for tetherto#1492 is the
spawn error handling in node-rpc-client.ts, which turns the
synchronous bare-runtime throw (previously an uncaught exception that
crashed the host) into a catchable BareRuntimeBinaryNotFoundError.
Every init reject path (timeout, IPC server error, synchronous spawn throw,
pre-handshake worker exit) repeated the same module-state reset, and only some
also closed the IPC server and unlinked the socket. Extract resetModuleState()
and teardownFailedInit() so all reject paths reset state and release the
worker/server/socket consistently, removing the drift and the resource leaks on
the paths that previously only nulled state.
@simon-iribarren simon-iribarren force-pushed the fix/qvac-18515-bare-runtime-binary-resolution branch from b627945 to 37a51b6 Compare June 8, 2026 08:07

@lauripiisang lauripiisang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you write a test that checks this new behavior through RPC init via lying about the platform, e.g. something like

const ogPlatform = process.platform;

Object.defineProperty(process, 'platform', { value: 'commodore64' });
// do your test
finally {
  Object.defineProperty(process, 'platform', { value: ogPlatform });
}

@arun-mani-j

arun-mani-j commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

could you write a test that checks this new behavior through RPC init via lying about the platform, e.g. something like

const ogPlatform = process.platform;

Object.defineProperty(process, 'platform', { value: 'commodore64' });
// do your test
finally {
  Object.defineProperty(process, 'platform', { value: ogPlatform });
}

spawn detects (https://github.com/holepunchto/bare-runtime/blob/main/index.js#L14) the platform and arch through the native OS handles (https://github.com/holepunchto/bare-os/blob/main/index.js#L12), so we need to fake the OS handle itself to cause this error no? 🤔

Ah wait, NVM. It also allows us to pass the opts, so we need a test where we spawn but with different opts that letting Bare decide it.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Tier-based Approval Status

**PR Tier:** TIER1

**Current Status:** ❌ PENDING

**Requirements:**
- 1 Team Member approval ❌ (0/1)
- 1 Team Lead OR Management approval ❌ (0/1)



---
*This comment is automatically updated when reviews change.*

@arun-mani-j arun-mani-j left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recommended solution comes up nicely when I passed wrong platform and arch.

Image

Since this only comes for pnpm users, I'm wondering if we should ask the users facing the issue to update their pnpm than us adding all these 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tier1 verified Authorize secrets / label-gate in PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Error on Windows: No binaries found for target 'win32-x64' on qvac serve openai

4 participants