Debug a failed build
When a setup step fails, keep the builder VM around and attach a shell to inspect it.
When buildSnapshot fails, it normally cleans up the builder VM and you lose your repro. Two options change that.
1. Stream the output
The fastest first move: turn on verbose: true and watch the failing step’s stdout/stderr live.
import { buildSnapshot, defineSnapshot } from "@beamhop/lightbox";
await buildSnapshot(
defineSnapshot({
name: "rust-ci",
image: "rust:1.82",
setup: [
"rustup component add clippy rustfmt",
"cargo install cargo-nextest --locked",
],
}),
{ verbose: true },
);
Each step’s stdout/stderr is forwarded to your terminal byte-for-byte (terminal escapes preserved). If a step has a description, it prints as a header before the step runs — useful for finding “which step blew up” in a long install log.
This often tells you the answer (missing package, network failure, permissions error) without going further.
2. Keep the builder around
If the error is silent or you need to inspect state inside the VM, set debugBuilder: true. Lightbox normally removes the builder sandbox in a finally block whether the build succeeded or failed; debugBuilder skips that cleanup.
import { buildSnapshot, defineSnapshot } from "@beamhop/lightbox";
await buildSnapshot(
defineSnapshot({
name: "rust-ci",
image: "rust:1.82",
setup: ["rustup component add clippy"],
}),
{ debugBuilder: true, verbose: true },
).catch(console.error);
Now the builder VM lives on as a sandbox named <snapshot-name>-builder. Attach to it from a second script:
import { connectToSandbox } from "@beamhop/lightbox";
const builder = await connectToSandbox("rust-ci-builder");
// Drop into an interactive shell to poke at the filesystem.
await builder.attachShell();
Or run individual commands:
const out = await builder.shell("ls -la /root && rustup show");
console.log(out.stdout());
When you’re done, clean up:
import { removeSandbox } from "@beamhop/lightbox";
await removeSandbox("rust-ci-builder", { force: true });
3. Bisect the setup steps
If the steps are long, comment out everything after the suspect step, re-run with debugBuilder: true, attach, and inspect what got written to disk. The snapshot won’t be created (you only get the build artifact when all steps succeed), but the builder filesystem reflects every successful step up to the failure.
See also
- Create a snapshot —
buildSnapshot+BuildOptionstable connectToSandbox- Troubleshooting — for known error messages and their fixes