Troubleshooting
Concrete causes and fixes for the errors and surprises you'll hit in practice.
Build / snapshot
Snapshot "X" already exists. Pass { overwrite: true }
You called buildSnapshot with { overwrite: false } (or are on a pre-0.2 version where force: false was the default). Drop the overwrite: false — buildSnapshot is idempotent by default and will overwrite for you.
setup step exited with code N: <script>
A setup step failed inside the builder VM. Two follow-ups:
- Re-run with
{ verbose: true }to see what the step printed. The exit code alone rarely tells you enough. - If verbose output isn’t conclusive, keep the builder alive with
{ debugBuilder: true }and attach to it — see Debug a failed build.
sync failed: <stderr>
The post-setup sync call (which flushes the page cache before snapshotting) failed inside the guest. Almost always a permissions or filesystem problem inside the image — e.g. a setup step left the filesystem read-only, or wrote enough that the VM ran out of memory. Inspect the builder with debugBuilder: true.
A setup step succeeds, but its writes are missing from the snapshot
This shouldn’t happen: buildSnapshot runs sync for you before snapshotting. If you see it anyway, double-check the step actually ran (look for a [lightbox] <description> log line) and not in a subshell that exited early.
Launch / sandbox
launching <name> hangs forever
Almost always the image pull on first use. The microsandbox SDK pulls images on demand and doesn’t stream pull progress through lightbox. Drop into the SDK if you need to see progress:
import { Sandbox } from "@beamhop/lightbox";
const progress = await Sandbox.builder("test").image("oven/bun").createDetachedWithPullProgress();
Invalid port mapping: NaN → NaN
You passed a non-numeric key or value to ports. The type is Record<number, number> for a reason — object keys are stringified in JS but must parse as numbers. Fix:
// ✗ silently NaN
ports: { "abc": 80 }
// ✓
ports: { 8080: 80 }
A sandbox refuses to stop / detach
If a process inside the guest is holding a tty, sb.stop() waits indefinitely. Use sb.kill() to force shutdown, or removeSandbox(name, { force: true }) to kill+remove in one step.
Mounts / volumes
Bind-mount silently doesn’t appear inside the guest
Two causes:
- The host path doesn’t exist. The SDK doesn’t auto-create host directories. Run
mkdir -p ./my-dirbefore launching. - The guest path is already occupied by snapshot content. Mount paths must not collide with files baked into the image. Use a fresh directory like
/workor/mnt/cache, andmkdir -pit in the snapshot’s setup steps if necessary.
A named volume isn’t persisting writes
The volume is mounted but the guest process is writing to a path outside the mount. Double-check: if you mount { "/cache": { volume: "npm-cache" } }, only writes under /cache go to the volume. Writes to /root/.npm go to the sandbox’s ephemeral upper layer and vanish when it’s removed.
ensureVolume ignored my quota change
ensureVolume is idempotent — quota and labels are only applied when the volume is created. To change a quota, remove the volume first:
import { removeVolume, ensureVolume } from "@beamhop/lightbox";
await removeVolume("npm-cache"); // wipes the volume contents
await ensureVolume("npm-cache", { quotaMib: 8192 }); // new quota
Process / lifecycle
My sandbox vanished when my script exited
launchSandbox ties the sandbox’s lifecycle to your process. If you don’t want that, call sb.detach() before exiting, or use runInSandbox for a scoped sandbox that’s stopped cleanly when the callback returns.
connectToSandbox throws “sandbox not found”
The name doesn’t match a running sandbox. Common causes: the launching script never called .detach() so the sandbox died on exit; the sandbox was removed; you used the snapshot name instead of the sandbox name. List what’s actually running:
import { Sandbox } from "@beamhop/lightbox";
const all = await Sandbox.list();
console.log(all.map(h => `${h.name} (${h.status})`));
Disk / artifacts
Where do things live?
| Artifact | Path |
|---|---|
| Snapshots | ~/.microsandbox/snapshots/<name> |
| Volumes | ~/.microsandbox/volumes/<name> |
| Pulled images | ~/.microsandbox/images/ |
| Runtime binaries | ~/.microsandbox/bin/, ~/.microsandbox/lib/ |
Removing the whole ~/.microsandbox/ directory resets everything (next call will re-extract the runtime and re-pull images).