Error Handling
One
Machineclass, two backends. These are the typed exceptions raised by thesmolmachinesSDK (Node) /smolmodule (Python), on either the local embedded engine (default) or the smolfleet cloud. They wrap the underlying engine/transport errors into a small typed hierarchy. The standalone hosted Cloud API returns HTTP status codes (including402,422,429,503) directly.
The SDK exposes four error classes, all subclasses of SmolError.
Error Types
| Error | Description |
|---|---|
SmolError | Base class for all SDK errors. Carries a machine-readable code. |
NotSupportedError | The active backend can’t serve this operation (e.g. a local-only call on cloud, or vice versa). code = "NOT_SUPPORTED". |
InvalidConfigError | A required configuration value is missing or invalid (a usage error). code = "INVALID_CONFIG". |
ExecutionError | A command ran but exited non-zero. Raised by assertSuccess() / assert_success(). code = "COMMAND_FAILED". |
Any error coming out of the native engine is normalized into a SmolError whose code is the engine’s bracketed code (e.g. KVM_UNAVAILABLE), so you can always branch on err.code or instanceof / isinstance.
Basic Error Handling
TypeScriptimport {
Machine,
SmolError,
NotSupportedError,
InvalidConfigError,
ExecutionError,
} from 'smolmachines';
try {
const machine = await Machine.create({ name: 'test' });
const result = await machine.exec(['some-command']);
result.assertSuccess();
} catch (error) {
if (error instanceof ExecutionError) {
console.log(`Command failed with exit code: ${error.exitCode}`);
console.log(`stderr: ${error.stderr}`);
} else if (error instanceof InvalidConfigError) {
console.log(`Invalid configuration: ${error.message}`);
} else if (error instanceof NotSupportedError) {
console.log(`Not supported on this backend: ${error.message}`);
} else if (error instanceof SmolError) {
console.log(`SDK error [${error.code}]: ${error.message}`);
} else {
throw error;
}
} Python from smol import (
Machine,
MachineConfig,
SmolError,
NotSupportedError,
InvalidConfigError,
ExecutionError,
)
try:
machine = Machine.create(MachineConfig(name="test"))
result = machine.exec(["some-command"])
result.assert_success()
except ExecutionError as e:
print(f"Command failed with exit code: {e.exit_code}")
print(f"stderr: {e.stderr}")
except InvalidConfigError as e:
print(f"Invalid configuration: {e}")
except NotSupportedError as e:
print(f"Not supported on this backend: {e}")
except SmolError as e:
print(f"SDK error [{e.code}]: {e}") SmolError
Every SDK error carries a machine-readable code. Wrapping arbitrary engine errors is done by wrap_native_error (Python), which parses the engine’s [CODE] message format into a SmolError.
class SmolError extends Error {
readonly code: string; // e.g. "NOT_SUPPORTED", "KVM_UNAVAILABLE"
} Python class SmolError(Exception):
code: str # e.g. "NOT_SUPPORTED", "KVM_UNAVAILABLE"
message: str ExecutionError
Raised when a command exits with a non-zero status — via assertSuccess() / assert_success().
class ExecutionError extends SmolError {
readonly exitCode: number;
readonly stdout: string;
readonly 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 class ExecutionError(SmolError):
command: list[str] | str
exit_code: int
stdout: str
stderr: str
try:
result = 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
ExecResult lets you check the outcome 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}`);
} Python result = 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}") Cleanup on Error
Always clean up the machine, even on error:
TypeScript// try/finally — there is no wrapper helper
const machine = await Machine.create({ name: 'cleanup-example' });
try {
await machine.exec(['risky-command']);
} finally {
await machine.delete();
} Python # Context manager (recommended) — deletes on exit, even on error
with Machine.create(MachineConfig(name="cleanup-example")) as machine:
machine.exec(["risky-command"])
# Or manually with try/finally
machine = Machine.create(MachineConfig(name="cleanup-example"))
try:
machine.exec(["risky-command"])
finally:
machine.delete()