Skip to content

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

typescript
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;
  }
}
python
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.

typescript
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}`);
  }
}
python
@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:

typescript
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}`);
}
python
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:

typescript
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']));
python
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:

typescript
// 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
python
# 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()