Skip to main content
One fact shapes everything else: box.exec(...) does not raise on a non-zero exit code. It returns an ExecResult and leaves the decision to you. The BoxliteError hierarchy covers the other failures.

Which failures actually raise

Practical takeaway: catch in the order specific subclasses (ExecError/TimeoutError/ParseError) -> BoxliteError -> standard RuntimeError/Error, otherwise low-level failures escape.

Handling all four in one place

The most concise, directly runnable robust execution pattern: run a command, check the exit code, raise ExecError on failure, and catch every possible failure.
Node version:

Parameters and Returns

Exception hierarchy

All SDK-layer errors inherit from BoxliteError, so except BoxliteError / instanceof BoxliteError catches every error the SDK actively raises.
Note: Python’s boxlite.TimeoutError is a subclass of BoxliteError, not Python’s built-in builtins.TimeoutError. If you import both, use an alias to disambiguate.

ExecError constructor parameters (Python)

ExecError constructor parameters (Node, positional)

ExecResult (the return value of box.exec(...), not an exception)

This is the core of robust error handling: check the return value first, then decide whether to raise.
Note: the stdout/stderr above apply to the ExecResult returned by the high-level SimpleBox.exec(...). If you use the native await (await box.exec(...)).wait() path, its ExecResult has only exit_code and error_message --- accessing .stdout/.stderr raises AttributeError. To get output, use SimpleBox.exec.
Wrap “check the exit code + raise ExecError” into a helper so that the upper layer only needs a single try/except style.

Distinguishing a timeout from a general failure

Key fact: when SimpleBox.exec(timeout=...) hits a timeout it does not raise boxlite.TimeoutError --- it terminates the process on the sandbox side with SIGTERM and returns ExecResult(exit_code == -15). So in the example below, sleep 5 / timeout=1.0 reaches print("exit_code:", result.exit_code) and prints -15; the except TimeoutError branch does not fire. Always detect a timeout by checking result.exit_code (negative = terminated by signal); do not rely on except TimeoutError. boxlite.TimeoutError mainly models a higher-level SDK “waited for a readiness state and timed out” (e.g. waiting for desktop/browser readiness), not an exec command timeout.

Troubleshooting

You try/except ExecError, but a command failure is not caught

Cause: box.exec(...) does not raise on a non-zero command exit; it returns ExecResult(exit_code != 0). Fix: after running, check result.exit_code (Node: result.exitCode) and raise ExecError(...) yourself on failure, or use the run_checked wrapper above.

A missing command (spawn failure) raises in both SDKs

Python: calling box.exec(...) for a nonexistent command raises RuntimeError, with a message like internal error: spawn_failed: internal error: build failed: ... executable '<cmd>' not found in $PATH. It does not return an ExecResult and is not a BoxliteError --- isinstance(err, BoxliteError) is False. So catch it with except RuntimeError. Node: SimpleBox.exec throws a bare Error on spawn failure, with a message containing spawn_failed, and err instanceof BoxliteError === false. Fix: on Python, catch spawn failures with except RuntimeError (placed after except BoxliteError); on Node, catch the non-BoxliteError bare Error in the else branch. Do not assume a missing command returns an ExecResult or is a BoxliteError.

from boxlite import TimeoutError collides with Python’s built-in TimeoutError

Cause: boxlite.TimeoutError is a subclass of BoxliteError --- the same name as builtins.TimeoutError but a different class. Fix: import with an alias:

Passing the volume string "ro"/"rw" raises TypeError

Error message: TypeError: argument 'volumes': 'str' object cannot be cast as 'bool'. Cause: the SDK’s third volume element is the bool read_only (True = read-only / False = read-write), not the CLI’s "ro"/"rw" string. Fix:
Note: only the CLI’s -v host:box:ro syntax uses ro/rw strings; the SDK API uses a bool, so do not confuse them.

Image pull failure / network instability

Error message: RuntimeError (not BoxliteError). Fix: catch with except RuntimeError and retry (retry needs network access and a correctly spelled image reference). Always be online for the first run.

Box fails to start: no virtualization

Cause: BoxLite needs Linux + KVM/hardware virtualization; macOS uses the built-in Hypervisor.framework (no /dev/kvm required); WSL2 needs KVM with the user in the kvm group. Without virtualization, the box cannot start. Symptom: entering async with SimpleBox(...) or the first exec raises RuntimeError (Python) / Error (Node); the process itself does not crash and can be caught and degraded with try/except. Fix: run in an environment that supports virtualization; in CI, confirm /dev/kvm is mounted and permissioned.

C SDK: the command failure code and the API call error code are two distinct concepts

In the C SDK you likewise distinguish two layers: the API call itself returns a BoxliteErrorCode (such as NotFound=2, InvalidArgument=5), while the command process’s exit code is reported separately in ExecResult.exit_code --- an API return of Ok does not mean the command succeeded. See examples/c/04_error_handling.c: