Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.microsandbox.dev/llms.txt

Use this file to discover all available pages before exploring further.

All SDKs surface typed errors so you can match on specific failure modes instead of parsing strings. Rust has an Error enum, TypeScript exposes a dedicated subclass per variant (use instanceof), Python provides dedicated exception classes, and Go provides an *Error value with an ErrorKind discriminator matched via m.IsKind(err, kind) or errors.As.

Matching errors

use microsandbox::{Sandbox, Error};

async fn get_or_create(name: &str) -> Result<Sandbox, Error> {
    match Sandbox::get(name).await {
        Ok(handle) => handle.start().await,
        Err(Error::SandboxNotFound(_)) => {
            Sandbox::builder(name).image("python").create().await
        }
        Err(e) => Err(e),
    }
}

match sb.exec("python", ["script.py"]).await {
    Ok(output) if output.status().success => {
        println!("{}", output.stdout()?);
    }
    Ok(output) => {
        eprintln!("Exit {}: {}", output.status().code, output.stderr()?);
    }
    Err(Error::ExecTimeout) => eprintln!("Timed out"),
    Err(Error::Runtime(msg)) => eprintln!("Runtime: {msg}"),
    Err(e) => return Err(e),
}

Spawn-time exec failures

exec() distinguishes between:
  • A program that ran and exited non-zero — the call returns an ExecOutput with a non-zero code. This is not an error in the SDK sense; it’s a normal result.
  • A program that never started — the binary doesn’t exist, isn’t executable, the working directory is unreachable, etc. This surfaces as a typed error variant: ExecFailed (Rust), ExecFailedError (TypeScript), ExecFailedError (Python).
The typed error carries a classified kind plus the underlying errno, so callers can branch on the cause and react. Common kinds: NotFound (binary missing on PATH), PermissionDenied, NotExecutable, BadCwd, BadArgs, ResourceLimit, UserSetupFailed, OutOfMemory, PtySetupFailed, Other.
use microsandbox::Error;
use microsandbox_protocol::exec::ExecFailureKind;

match sb.exec("nonexistent", []).await {
    Ok(output) => { /* program ran, check output.status() */ }
    Err(Error::ExecFailed(payload)) => {
        match payload.kind {
            ExecFailureKind::NotFound => {
                eprintln!("Binary not found on PATH: {}", payload.message);
            }
            ExecFailureKind::PermissionDenied => {
                eprintln!("Not executable (chmod +x?): {}", payload.message);
            }
            kind => {
                eprintln!("Spawn failed ({:?}): {}", kind, payload.message);
            }
        }
        // payload.errno, payload.errno_name, payload.stage are also available
    }
    Err(e) => return Err(e),
}
The CLI maps these kinds to POSIX-style exit codes: 127 for NotFound, 126 for PermissionDenied and NotExecutable, 1 otherwise. SDK callers reading the error directly don’t need to think about exit codes — branch on kind instead.

Name conflicts

Creating a sandbox with a name that’s already in use (and without replace) surfaces a typed error you can branch on to decide whether to recover (resume the existing one, regenerate the name, etc.).
use microsandbox::Error;

match Sandbox::builder("worker").image("alpine").create().await {
    Ok(sb) => { /* ... */ }
    Err(Error::SandboxAlreadyExists(name)) => {
        eprintln!("sandbox {name} already exists; resume or pass .replace()");
    }
    Err(e) => return Err(e),
}
Pass replace() / replace=True / --replace / WithReplace() to stop the existing sandbox and recreate it. See Replace existing for the grace-period knob.

Sandbox start failures

When a sandbox process exits before the agent relay is ready (mount errors, missing rootfs, network setup failures), the SDK surfaces a typed BootStart / BootStartError. The payload carries the failure stage and errno so callers can recover or report cleanly.
use microsandbox::Error;
use microsandbox_runtime::boot_error::BootErrorStage;

match Sandbox::builder("svc").image("alpine").create().await {
    Ok(sb) => { /* ... */ }
    Err(Error::BootStart { name, err }) => {
        eprintln!("Sandbox {name:?} failed at stage {:?}: {}", err.stage, err.message);
        if matches!(err.stage, BootErrorStage::Mount) {
            eprintln!("Hint: a host volume path may not exist.");
        }
    }
    Err(e) => return Err(e),
}
The CLI prepends the same payload as a styled error: block before any captured log output, so you see “what went wrong + a hint” inline. SDK callers get the structured payload to make their own decisions.

Resource cleanup

Sandboxes hold compute resources, so release them when done. In Rust, Drop handles cleanup when the sandbox goes out of scope. In TypeScript, prefer await using (Node 22+) which calls Sandbox.stop() automatically when the binding leaves scope. In Go, pair every CreateSandbox with a defer that calls StopAndWait + Close.
use microsandbox::Sandbox;

// Sandbox implements Drop, so resources are released when `sb` goes out of scope.
// For explicit control, call stop() or kill().
{
    let sb = Sandbox::builder("temp")
        .image("python")
        .create()
        .await?;

    let output = sb.exec("python", ["-c", "print('hello')"]).await?;
} // sb is dropped here, resources are cleaned up