import {
SandboxError,
TimeoutError,
NotFoundError,
AuthenticationError,
InvalidArgumentError,
NotEnoughSpaceError,
TemplateError,
BuildError,
FileUploadError,
GitAuthError,
GitUpstreamError,
CommandExitError,
} from '@declaw/sdk';
All SDK errors extend SandboxError, which extends the built-in Error. You can catch SandboxError to handle any Declaw-specific failure, or catch a specific subclass for granular control.
Error hierarchy
Error
└── SandboxError
├── TimeoutError
├── NotFoundError
├── AuthenticationError
├── InvalidArgumentError
├── NotEnoughSpaceError
├── FileUploadError
├── GitAuthError
├── GitUpstreamError
├── CommandExitError
└── TemplateError
└── BuildError
SandboxError
Base class for all Declaw errors.
class SandboxError extends Error {
sandboxId?: string;
name: string; // set to the subclass name for `instanceof`-free discrimination
constructor(message: string, opts?: { sandboxId?: string });
}
Properties
| Property | Type | Description |
|---|
sandboxId | string | undefined | The sandbox ID involved, when available. |
name | string | Error subclass name (e.g. 'TimeoutError'). |
TimeoutError
Thrown when an operation exceeds its configured timeout.
import { Sandbox, TimeoutError } from '@declaw/sdk';
try {
const result = await sbx.commands.run('sleep 300', { timeout: 10 });
} catch (err) {
if (err instanceof TimeoutError) {
console.error('Timed out in sandbox:', err.sandboxId);
}
}
NotFoundError
Thrown when the sandbox or a requested resource does not exist (HTTP 404).
import { Sandbox, NotFoundError } from '@declaw/sdk';
try {
const sbx = await Sandbox.connect('nonexistent-id', { apiKey: 'key', domain: 'host:8080' });
} catch (err) {
if (err instanceof NotFoundError) {
console.error('Sandbox not found');
}
}
AuthenticationError
Thrown when the API key is missing or invalid (HTTP 401/403).
import { Sandbox, AuthenticationError } from '@declaw/sdk';
try {
const sbx = await Sandbox.create({ apiKey: 'bad-key', domain: 'host:8080' });
} catch (err) {
if (err instanceof AuthenticationError) {
console.error('Invalid API key');
}
}
InvalidArgumentError
Thrown when a method receives an argument that fails validation. For example,
createTransformationRule() throws this for invalid regex patterns or
disallowed sandbox IDs containing special characters.
import { createTransformationRule, InvalidArgumentError } from '@declaw/sdk';
try {
createTransformationRule({ match: '(a+)+', replace: 'safe' });
} catch (err) {
if (err instanceof InvalidArgumentError) {
console.error('Validation failed:', err.message);
}
}
NotEnoughSpaceError
Thrown when the sandbox filesystem is full and a write fails.
import { NotEnoughSpaceError } from '@declaw/sdk';
try {
await sbx.files.write('/data/large.bin', hugeBuffer);
} catch (err) {
if (err instanceof NotEnoughSpaceError) {
console.error('Sandbox disk full');
}
}
CommandExitError
Thrown by CommandHandle.wait() when the process exits with a non-zero code.
Contains the full stdout, stderr, and exit code.
class CommandExitError extends SandboxError {
exitCode: number;
stdout: string;
stderr: string;
constructor(
message: string,
opts: { sandboxId?: string; exitCode: number; stdout: string; stderr: string },
);
}
Properties
| Property | Type | Description |
|---|
exitCode | number | The process exit code. |
stdout | string | Accumulated stdout output. |
stderr | string | Accumulated stderr output. |
import { CommandExitError } from '@declaw/sdk';
const handle = await sbx.commands.run('python3 bad_script.py', { background: true });
try {
const result = await handle.wait();
} catch (err) {
if (err instanceof CommandExitError) {
console.error(`Exit ${err.exitCode}: ${err.stderr}`);
}
}
CommandExitError is thrown only by handle.wait(). The foreground
sbx.commands.run() (without background: true) returns a CommandResult
with a non-zero exitCode rather than throwing. Check result.exitCode
manually in foreground mode.
TemplateError
Base class for template-related errors.
BuildError
Thrown when Template.build() fails.
import { Template, BuildError } from '@declaw/sdk';
try {
const info = await Template.build(template, 'alias', { apiKey: 'key', domain: 'host:8080' });
} catch (err) {
if (err instanceof BuildError) {
console.error('Build failed:', err.message);
}
}
FileUploadError
Thrown when a file upload fails (e.g. a network error during a write operation).
GitAuthError
Thrown when git operations inside the sandbox fail due to authentication errors.
GitUpstreamError
Thrown when git operations fail due to upstream repository errors.
Catching all Declaw errors
import { SandboxError } from '@declaw/sdk';
let sbx;
try {
sbx = await Sandbox.create({ apiKey: 'key', domain: 'host:8080' });
const result = await sbx.commands.run('my-command');
console.log(result.stdout);
} catch (err) {
if (err instanceof SandboxError) {
console.error(`Declaw error [${err.name}]: ${err.message}`);
if (err.sandboxId) {
console.error('Sandbox ID:', err.sandboxId);
}
} else {
throw err; // re-throw unexpected errors
}
} finally {
await sbx?.kill();
}
Using error.name for discrimination
Because name is set on every subclass, you can discriminate without instanceof:
try {
await sbx.kill();
} catch (err) {
if (err instanceof SandboxError) {
switch (err.name) {
case 'NotFoundError':
console.log('Already gone');
break;
case 'TimeoutError':
console.log('Kill timed out');
break;
default:
throw err;
}
}
}
Retry patterns
Manual retry with exponential back-off
import { Sandbox, TimeoutError } from '@declaw/sdk';
async function runWithRetry(
sbx: Sandbox,
cmd: string,
retries = 3,
): Promise<string> {
for (let attempt = 0; attempt < retries; attempt++) {
try {
const result = await sbx.commands.run(cmd, { timeout: 30 });
return result.stdout;
} catch (err) {
if (err instanceof TimeoutError && attempt < retries - 1) {
const delay = 1000 * Math.pow(2, attempt);
await new Promise((r) => setTimeout(r, delay));
} else {
throw err;
}
}
}
throw new Error('unreachable');
}
Handle non-zero exit codes in foreground mode
const result = await sbx.commands.run('python3 risky.py');
if (result.exitCode !== 0) {
console.error(`Failed (exit ${result.exitCode}):`, result.stderr);
} else {
console.log(result.stdout);
}
Cleanup on error using await using
await using sbx = await Sandbox.create({ apiKey: 'key', domain: 'host:8080' });
// sbx.close() is guaranteed on block exit even if an error is thrown
const result = await sbx.commands.run('npm test');
await sbx.kill();