Skip to main content
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
PropertyTypeDescription
sandboxIdstring | undefinedThe sandbox ID involved, when available.
namestringError 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
PropertyTypeDescription
exitCodenumberThe process exit code.
stdoutstringAccumulated stdout output.
stderrstringAccumulated 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();