Skip to content

testBindingBinary fails on Windows: forked child doesn't send 'ready' with ignore stdio (Vulkan prebuilt falls back to source build) #638

Description

@luluxiang06

Environment

  • OS: Windows 11 (10.0.26200)
  • Node: v24.14.0
  • node-llama-cpp: 3.18.1 and 3.19.1 (both affected)
  • GPU: Intel Iris Xe Graphics (Vulkan available, `vulkan-1.dll` present in System32)

Problem

`getLlama({ gpu: "vulkan" })` fails with:
```
Failed to load a prebuilt binary for platform "win" "x64" with Vulkan support, falling back to building from source.
Error: Binding binary test failed to run a test process via file ".../testBindingBinary.js"
```

The Vulkan prebuilt binary itself is fine — `require(llama-addon.node)` + `loadBackends()` + `init()` + `getGpuType()` + `ensureGpuDeviceIsSupported()` all succeed when called directly in a child process. The failure is in `testBindingBinary.js`'s child process orchestration, not the binary.

Root cause

`testBindingBinary` forks a child process to test the binary before loading it. When `pipeOutputOnNode=false` (the default), the child is spawned with `stdio: ["ignore","ignore","ignore","ipc"]` (testBindingBinary.js:96-98). On Windows, the forked child process exits without sending the `ready` IPC message, so `forkSucceeded` stays `false` and the parent reports `"Binding binary test failed to run a test process"` (testBindingBinary.js:167).

When `pipeOutputOnNode=true` (pipe stdio instead of ignore), the child sends `ready` normally and the test passes — confirmed by calling `getLlamaForOptions({gpu:"vulkan"}, {pipeBinaryTestErrorLogs: true})`, which succeeds.

Workaround that works

`getLlamaForOptions({ gpu: "vulkan" }, { pipeBinaryTestErrorLogs: true })` succeeds — `pipeBinaryTestErrorLogs` is forwarded as `pipeOutputOnNode` to `testBindingBinary`, switching to pipe stdio.

However, the public `getLlama()` API does not pass `pipeBinaryTestErrorLogs` to `getLlamaForOptions` — getLlama.js:94 calls `getLlamaForOptions(options ?? {})` with no second argument, so the option defaults to `false`. Users cannot benefit from the workaround through the public API.

An odd detail: calling `getLlamaForOptions` directly (via file:// import) with the same options succeeds, but going through `getLlama` (which calls `getLlamaForOptions` with the same args) still fails — suggesting the issue may be sensitive to the ESM module instance / call context, not just the option value.

Suggested fix

Either:

  1. Default `pipeBinaryTestErrorLogs` to `true` on Windows in `getLlama` (so the public API benefits from pipe stdio), or
  2. Investigate why `stdio: "ignore"` causes the forked child to not send `ready` on Windows — possibly a Node.js fork/IPC issue with ignored stdio on Windows.

Reproduction

```js
import { getLlama } from "node-llama-cpp";
// Fails on Windows (falls back to building from source, or to CPU with gpu:"auto"):
await getLlama({ gpu: "vulkan" });

// Works (uses the pipeBinaryTestErrorLogs workaround):
const path = require("path");
const indexPath = require.resolve("node-llama-cpp");
const glPath = path.join(path.dirname(path.dirname(indexPath)), "dist", "bindings", "getLlama.js").split(path.sep).join("/");
const mod = await import("file:///" + glPath);
const llama = await mod.getLlamaForOptions({ gpu: "vulkan" }, { pipeBinaryTestErrorLogs: true });
```

`inspect gpu` confirms Vulkan is available and detected (Intel Iris Xe, 15.8GB VRAM), so this is not a driver/runtime issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions