> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxlite.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors & Metrics

> Python SDK error handling and metrics

BoxLite provides a structured exception hierarchy for error handling and metrics classes for monitoring resource usage across boxes.

## Error Types

```python theme={null}
from boxlite import BoxliteError, ExecError, TimeoutError, ParseError
```

### Exception Hierarchy

```text theme={null}
BoxliteError (base)
├── ExecError       # Command execution failed
├── TimeoutError    # Operation timed out
└── ParseError      # Output parsing failed
```

### `BoxliteError`

Base exception for all BoxLite errors. Catch this to handle any BoxLite-specific error.

```python theme={null}
try:
    async with SimpleBox(image="invalid:image") as box:
        pass
except BoxliteError as e:
    print(f"BoxLite error: {e}")
```

### `ExecError`

Raised when a command execution fails (non-zero exit code).

| Attribute   | Type  | Description             |
| ----------- | ----- | ----------------------- |
| `command`   | `str` | The command that failed |
| `exit_code` | `int` | Non-zero exit code      |
| `stderr`    | `str` | Standard error output   |

```python theme={null}
try:
    result = await box.exec("false")  # Exit code 1
except ExecError as e:
    print(f"Command: {e.command}")
    print(f"Exit code: {e.exit_code}")
    print(f"Stderr: {e.stderr}")
```

### `TimeoutError`

Raised when an operation times out.

```python theme={null}
try:
    await computer.wait_until_ready(timeout=5)
except TimeoutError:
    print("Desktop did not become ready in time")
```

### `ParseError`

Raised when output parsing fails.

```python theme={null}
try:
    x, y = await computer.cursor_position()
except ParseError:
    print("Failed to parse cursor position")
```

## Core Error Variants

BoxLite uses a centralized error enum internally. These are surfaced as `BoxliteError` exceptions in Python with descriptive messages.

### `UnsupportedEngine`

Platform or hypervisor not supported.

**Cause:**

* Running on Windows
* Running on Intel Mac
* KVM not available on Linux
* Hypervisor.framework not available on macOS

**Example:**

```text theme={null}
Error: unsupported engine kind
```

**Solution:**

* Use supported platform (macOS ARM64, Linux x86\_64/ARM64)
* Verify hypervisor availability:
  * Linux: `grep -E 'vmx|svm' /proc/cpuinfo`
  * macOS: Ensure macOS 12+ on Apple Silicon

### `Engine(String)`

Hypervisor or VM engine error.

**Cause:**

* KVM module not loaded
* Insufficient permissions for `/dev/kvm`
* Hypervisor.framework error
* VM creation failed

**Example:**

```text theme={null}
Error: engine reported an error: KVM is not available
```

**Solution:**

```bash theme={null}
# Linux: Load KVM module
sudo modprobe kvm kvm_intel  # or kvm_amd

# Linux: Check /dev/kvm permissions
ls -l /dev/kvm
sudo chmod 666 /dev/kvm

# Linux: Add user to kvm group
sudo usermod -aG kvm $USER
# (logout and login required)
```

### `Config(String)`

Invalid box configuration.

**Cause:**

* Invalid CPU count (\< 1 or > host CPUs)
* Invalid memory size (\< 128 or > 65536)
* Invalid paths in volumes
* Invalid port numbers

**Example:**

```text theme={null}
Error: configuration error: CPU count must be between 1 and 8
```

**Solution:**

* Verify configuration parameters are within valid ranges
* Check file paths exist for volume mounts
* Ensure port numbers are valid (1-65535)

### `Storage(String)`

Filesystem or disk operation error.

**Cause:**

* Disk full (`~/.boxlite` partition)
* Permission denied writing to `~/.boxlite`
* Disk image creation failed
* QCOW2 operation failed

**Example:**

```text theme={null}
Error: storage error: No space left on device
```

**Solution:**

```bash theme={null}
# Check disk space
df -h ~/.boxlite

# Check permissions
ls -ld ~/.boxlite
chmod 755 ~/.boxlite

# Clean up old boxes
# (manually remove ~/.boxlite/boxes/*)
```

### `Image(String)`

OCI image pull or extraction error.

**Cause:**

* Network connectivity issues
* Invalid image name or tag
* Registry authentication required
* Image not found in registry
* Corrupted image layers

**Example:**

```text theme={null}
Error: images error: failed to pull image: 404 Not Found
```

**Solution:**

```bash theme={null}
# Verify image exists
docker pull <image>

# Check network connectivity
ping registry-1.docker.io

# Authenticate for private images
docker login

# Clear image cache if corrupted
rm -rf ~/.boxlite/images/*
```

### `Portal(String)`

Host-guest communication error (gRPC over vsock).

**Cause:**

* Guest agent not responding
* vsock connection failed
* gRPC timeout
* Guest initialization failed

**Example:**

```text theme={null}
Error: portal error: connection timeout
```

**Solution:**

* Enable debug logging: `RUST_LOG=debug`
* Check if box is running: `box.info().status`
* Restart box: `box.stop()` and recreate
* Report issue with logs if persists

### `Network(String)`

Network configuration or connectivity error.

**Cause:**

* gvproxy not running or crashed
* Port already in use
* Network backend initialization failed

**Example:**

```text theme={null}
Error: network error: bind: address already in use
```

**Solution:**

```bash theme={null}
# Check port availability
lsof -i :8080

# Stop conflicting process or use different port
ports=[(8081, 80, "tcp")]

# Verify gvproxy binary exists
ls ~/.boxlite/gvproxy/
```

### `Execution(String)`

Command execution error.

**Cause:**

* Command not found in image
* Command crashed or killed
* Execution timeout
* Streaming I/O error

**Example:**

```text theme={null}
Error: Execution error: command not found: python3
```

**Solution:**

* Verify command exists in image:

  ```python theme={null}
  result = await box.exec("which", "python3")
  ```

* Check exit code and stderr:

  ```python theme={null}
  result = await box.exec("command")
  if result.exit_code != 0:
      print(f"Failed: {result.stderr}")
  ```

### `Internal(String)`

Internal BoxLite error.

**Cause:**

* Unexpected internal state
* I/O error
* JSON parsing error
* Unhandled edge case

**Example:**

```text theme={null}
Error: internal error: unexpected state transition
```

**Solution:**

* Enable debug logging: `RUST_LOG=debug python script.py`
* Report issue with full logs to GitHub
* Include BoxLite version, platform, and reproduction steps

### `NotFound(String)`

Box or resource not found.

**Cause:**

* Box ID does not exist
* Box was removed
* Image not in cache

**Example:**

```text theme={null}
Error: box not found: 01JJNH8...
```

**Solution:**

* List all boxes: `runtime.list()`
* Verify box ID is correct
* Create new box if needed

### `AlreadyExists(String)`

Box or resource already exists.

**Cause:**

* Duplicate box creation attempt
* Port already forwarded

**Example:**

```text theme={null}
Error: already exists: box with this ID exists
```

**Solution:**

* Use existing box: `runtime.get(box_id)`
* Remove existing box: `box.remove()`
* Use different configuration (e.g., different port)

### `InvalidState(String)`

Box is in wrong state for requested operation.

**Cause:**

* Executing command on stopped box
* Stopping already stopped box
* Restarting box that never started

**Example:**

```text theme={null}
Error: invalid state: cannot execute on stopped box
```

**Solution:**

* Check box status: `info = await box.info(); print(info.status)`
* Restart box if stopped: `runtime.get(box_id)` (may auto-restart)
* Create new box if needed

## Error Handling Patterns

### Basic Error Handling

```python theme={null}
import boxlite

async def safe_execution():
    try:
        async with boxlite.SimpleBox(image="python:slim") as box:
            result = await box.exec("python", "script.py")

            # Check exit code
            if result.exit_code != 0:
                print(f"Command failed: {result.stderr}")
                return

    except Exception as e:
        # All BoxLite errors are raised as Python exceptions
        print(f"Error: {e}")

        # Enable debug logging for details
        # RUST_LOG=debug python script.py
```

### Catching Specific Errors

```python theme={null}
from boxlite import SimpleBox, BoxliteError, ExecError, TimeoutError

async def handle_errors():
    try:
        async with SimpleBox(image="python:slim") as box:
            result = await box.exec("python", "-c", "import nonexistent")
    except ExecError as e:
        print(f"Execution failed (exit {e.exit_code}): {e.stderr}")
    except TimeoutError:
        print("Operation timed out")
    except BoxliteError as e:
        print(f"BoxLite error: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")
```

### Retry Pattern

```python theme={null}
import asyncio
from boxlite import SimpleBox, BoxliteError

async def with_retry(max_retries=3):
    for attempt in range(max_retries):
        try:
            async with SimpleBox(image="python:slim") as box:
                result = await box.exec("python", "-c", "print('success')")
                return result.stdout
        except BoxliteError as e:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt  # Exponential backoff
                print(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
                await asyncio.sleep(wait_time)
            else:
                raise
```

## Metrics

### `boxlite.RuntimeMetrics`

Aggregate metrics across all boxes managed by a runtime instance.

| Field           | Type  | Description              |
| --------------- | ----- | ------------------------ |
| `boxes_created` | `int` | Total boxes created      |
| `boxes_failed`  | `int` | Total boxes that failed  |
| `running`       | `int` | Currently running boxes  |
| `commands`      | `int` | Total command executions |
| `errors`        | `int` | Total errors             |

```python theme={null}
runtime = Boxlite.default()
metrics = await runtime.metrics()

print(f"Boxes created: {metrics.boxes_created}")
print(f"Boxes failed: {metrics.boxes_failed}")
print(f"Running: {metrics.running}")
print(f"Commands: {metrics.commands}")
```

### `boxlite.BoxMetrics`

Per-box resource usage metrics. Retrieved via `box.metrics()`.

| Field             | Type            | Description                        |
| ----------------- | --------------- | ---------------------------------- |
| `commands`        | `int`           | Total commands executed            |
| `errors`          | `int`           | Total errors                       |
| `total_create_ms` | `int \| None`   | Time to create box in milliseconds |
| `guest_boot_ms`   | `int \| None`   | Guest boot time in milliseconds    |
| `cpu`             | `float \| None` | CPU usage                          |
| `mem`             | `int \| None`   | Memory usage in bytes              |

```python theme={null}
metrics = await box.metrics()

print(f"Commands: {metrics.commands}")
print(f"Create time: {metrics.total_create_ms}ms")
print(f"Memory: {metrics.mem / (1024**2):.2f} MB")
```

<Note>
  `box.metrics()` is available on the low-level `Box` object (returned by `runtime.create()`). `SimpleBox` does not expose a `metrics()` method.
</Note>

### Monitoring Example

```python theme={null}
import asyncio
from boxlite import Boxlite, BoxOptions

async def monitor_box():
    runtime = Boxlite.default()
    box = await runtime.create(BoxOptions(image="python:slim"))

    try:
        # Start a workload
        execution = await box.exec("python", ["-c", """
import time
data = []
for i in range(100):
    data.append('x' * 10000)
    time.sleep(0.1)
"""])

        # Monitor metrics while running
        for _ in range(10):
            metrics = await box.metrics()
            print(f"Commands: {metrics.commands} | "
                  f"Memory: {metrics.mem / (1024**2):.1f} MB")
            await asyncio.sleep(1)

        await execution.wait()
    finally:
        await box.stop()
```

### Runtime-wide Monitoring

```python theme={null}
from boxlite import Boxlite, BoxOptions

async def runtime_monitoring():
    runtime = Boxlite.default()

    # Create several boxes
    boxes = []
    for i in range(3):
        box = await runtime.create(BoxOptions(image="alpine:latest"))
        boxes.append(box)

    # Check runtime metrics
    metrics = await runtime.metrics()
    print(f"Running: {metrics.running}")
    print(f"Total created: {metrics.boxes_created}")

    # Clean up
    for box in boxes:
        await box.stop()

    # Check again
    metrics = await runtime.metrics()
    print(f"Running: {metrics.running}")
```

## See Also

* [Python SDK Overview](/reference/python/index) - Runtime management, installation
* [Box Types](/reference/python/box-types) - SimpleBox, CodeBox, and other box types
* [Execution](/reference/python/execution) - Command execution reference
* [Configuration Reference](/reference/index) - BoxOptions details and error codes
