oven-sh/bun · error · BuildError

Cannot exec: build mode produced no executable

Error message

Cannot exec: build mode produced no executable

What it means

After a successful ninja build, scripts/build.ts execs the freshly linked binary with any trailing positional args. This BuildError fires when result.output.exe is undefined — the chosen mode builds artifacts rather than a host-runnable executable (the comment notes exe is bun-debug in debug, bun-profile in release).

Source

Thrown at scripts/build.ts:293

      process.exit(ninja.status ?? 1);
    }

    if (args.execArgs.length === 0) {
      // Closing line on success: when restat prunes most of the graph
      // (local WebKit no-op shows `[1/555] build WebKit` then silence),
      // it's not obvious ninja finished vs. stalled. This disambiguates.
      // Targets named when explicit so it's clear what was actually built.
      const what = args.ninjaTargets.length > 0 ? ` ${args.ninjaTargets.map(t => nameColor(t)).join(", ")}` : "";
      status(`[build]${what} done`);
      process.exit(0);
    }

    // Exec the built binary. result.output.exe is the linked (unstripped)
    // binary — bun-debug for debug, bun-profile for release. That's the one
    // you want for dev iteration (has symbols + assertions in debug).
    const exe = result.output.exe;
    if (exe === undefined) {
      throw new BuildError("Cannot exec: build mode produced no executable", {
        hint: `mode=${result.cfg.mode} builds artifacts, not a runnable binary. Drop the positional args or use --profile=debug.`,
      });
    }
    const child = spawnSync(exe, args.execArgs, { stdio: "inherit" });
    if (child.error) {
      throw new BuildError(`Failed to exec ${exe}`, { cause: child.error });
    }
    // Signal death: re-raise so our parent sees the same signal (shells
    // show "Segmentation fault" etc. based on this, not exit code).
    if (child.signal) {
      process.kill(process.pid, child.signal);
      return;
    }
    process.exit(child.status ?? 0);
  }
}

/**

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Drop the positional/exec args — build only: bun run build windows-x64
  2. Or pick a runnable profile: add --profile=debug (produces bun-debug) before the exec args
  3. Check the mode named in the hint to confirm which profile produced artifacts

Example fix

# before — exec args on an artifact-only mode
$ bun run build windows-x64 test/js/foo.test.ts
BuildError: Cannot exec: build mode produced no executable

# after — build only, no exec args
$ bun run build windows-x64
# or run tests with a runnable profile
$ bun bd test js/foo.test.ts
Defensive patterns

Strategy: validation

Validate before calling

// before build-then-exec, make sure the mode yields a host-runnable binary
const profile = process.argv.find(a => a.startsWith("--profile="))?.slice(9) ?? "debug";
const artifactOnly = /windows-(x64|arm64)/.test(profile); // cross-compile from non-Windows hosts
const hasExecArgs = process.argv.slice(2).some(a => !a.startsWith("-"));
if (hasExecArgs && artifactOnly) {
  console.error("refusing: artifact-only profile cannot exec; remove trailing args");
  process.exit(2);
}

Prevention

When it happens

Trigger: Passing exec args to an artifact-only mode — e.g. bun run build windows-x64 <args...> on a non-Windows host (cross-compile output cannot run here), or a target/profile whose output is objects or a library rather than the bun binary.

Common situations: Cross-compiling Windows builds from Linux/macOS and leaving trailing args on the command; using --target with positional exec args; copying a `bun bd test ...` invocation onto a cross-compile profile.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/0ba5f3a6fc7914c2. Report an issue: GitHub.