Error Handling

The SDK provides typed exceptions for different error conditions.

Error Types

ErrorDescription
SmolvmErrorBase class for all SDK errors
ConnectionErrorCannot connect to server
TimeoutErrorRequest timed out
NotFoundErrorResource not found (404)
ConflictErrorResource conflict (409)
BadRequestErrorInvalid request (400)
InternalErrorServer error (500)
ExecutionErrorCommand failed (non-zero exit)

Basic Error Handling

import {
  SmolvmError,
  NotFoundError,
  ConflictError,
  TimeoutError,
  ConnectionError,
  ExecutionError,
} from 'smolvm';

try {
  const machine = await Machine.create({ name: 'test' });
  const result = await machine.exec(['some-command']);
  result.assertSuccess();
} catch (error) {
  if (error instanceof ConnectionError) {
    console.log('Cannot connect to smolvm server');
    console.log('Is the server running? Try: smolvm serve');
  } else if (error instanceof NotFoundError) {
    console.log('Machine not found');
  } else if (error instanceof ConflictError) {
    console.log('Machine already exists');
  } else if (error instanceof TimeoutError) {
    console.log('Request timed out');
  } else if (error instanceof ExecutionError) {
    console.log(`Command failed with exit code: ${error.exitCode}`);
    console.log(`stderr: ${error.stderr}`);
  } else if (error instanceof SmolvmError) {
    console.log(`SDK error: ${error.message}`);
  } else {
    throw error;
  }
}
from smolvm import (
    SmolvmError,
    NotFoundError,
    ConflictError,
    TimeoutError,
    ConnectionError,
    ExecutionError,
)

try:
    machine = await Machine.create(MachineConfig(name="test"))
    result = await machine.exec(["some-command"])
    result.assert_success()
except ConnectionError:
    print("Cannot connect to smolvm server")
    print("Is the server running? Try: smolvm serve")
except NotFoundError:
    print("Machine not found")
except ConflictError:
    print("Machine already exists")
except TimeoutError:
    print("Request timed out")
except ExecutionError as e:
    print(f"Command failed with exit code: {e.exit_code}")
    print(f"stderr: {e.stderr}")
except SmolvmError as e:
    print(f"SDK error: {e}")

ExecutionError

Raised when a command exits with a non-zero status.

interface ExecutionError extends SmolvmError {
  exitCode: number;
  stdout: string;
  stderr: string;
}

try {
  const result = await machine.exec(['false']);  // exits with 1
  result.assertSuccess();  // throws ExecutionError
} catch (error) {
  if (error instanceof ExecutionError) {
    console.log(`Exit code: ${error.exitCode}`);
    console.log(`Output: ${error.stdout}`);
    console.log(`Error: ${error.stderr}`);
  }
}
@dataclass
class ExecutionError(SmolvmError):
    exit_code: int
    stdout: str
    stderr: str

try:
    result = await machine.exec(["false"])  # exits with 1
    result.assert_success()  # raises ExecutionError
except ExecutionError as e:
    print(f"Exit code: {e.exit_code}")
    print(f"Output: {e.stdout}")
    print(f"Error: {e.stderr}")

Handling Without Exceptions

Check results without throwing:

const result = await machine.exec(['maybe-fails']);

if (!result.success) {
  console.log(`Command failed with code ${result.exitCode}`);
  console.log(`stderr: ${result.stderr}`);
} else {
  console.log(`Success: ${result.stdout}`);
}
result = await machine.exec(["maybe-fails"])

if not result.success:
    print(f"Command failed with code {result.exit_code}")
    print(f"stderr: {result.stderr}")
else:
    print(f"Success: {result.stdout}")

Retry Logic

Implement retry for transient failures:

import { TimeoutError, ConnectionError } from 'smolvm';

async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  delay = 1000
): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof TimeoutError || error instanceof ConnectionError) {
        if (i === maxRetries - 1) throw error;
        await new Promise(r => setTimeout(r, delay * (i + 1)));
      } else {
        throw error;
      }
    }
  }
  throw new Error('Unreachable');
}

const result = await withRetry(() => machine.exec(['slow-command']));
import asyncio
from smolvm import TimeoutError, ConnectionError

async def with_retry(fn, max_retries=3, delay=1.0):
    for i in range(max_retries):
        try:
            return await fn()
        except (TimeoutError, ConnectionError):
            if i == max_retries - 1:
                raise
            await asyncio.sleep(delay * (i + 1))

result = await with_retry(lambda: machine.exec(["slow-command"]))

Cleanup on Error

Always clean up resources, even on error:

// Using try/finally
const machine = await Machine.create({ name: 'cleanup-example' });
try {
  await machine.exec(['risky-command']);
} finally {
  await machine.stop();
  await machine.delete();
}

// Or use withMachine helper
import { withMachine } from 'smolvm';

await withMachine({ name: 'auto-cleanup' }, async (machine) => {
  await machine.exec(['risky-command']);
});
// Automatically cleaned up
# Using context manager (recommended)
async with Machine(config) as machine:
    await machine.start()
    await machine.exec(["risky-command"])
# Automatically cleaned up, even on error

# Or manually with try/finally
machine = await Machine.create(config)
try:
    await machine.exec(["risky-command"])
finally:
    await machine.stop()
    await machine.delete()
    await machine.close()