> ## Documentation Index
> Fetch the complete documentation index at: https://docs.declaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Error class hierarchy for the TypeScript SDK, how to catch specific errors, and retry patterns for transient failures.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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).

```typescript theme={null}
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).

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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. |

```typescript theme={null}
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}`);
  }
}
```

<Note>
  `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.
</Note>

***

## `TemplateError`

Base class for template-related errors.

***

## `BuildError`

Thrown when `Template.build()` fails.

```typescript theme={null}
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

```typescript theme={null}
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`:

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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`

```typescript theme={null}
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();
```
