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

# Errors

> Sailbox and image error taxonomy

Every SDK failure carries the same taxonomy: catch the base `SailError`
for everything, or match a specific failure.

<CodeGroup>
  ```python Python theme={null}
  import sail

  try:
      sb = sail.Sailbox.create(app=app, name="box")
      result = sb.exec("false", timeout=5).wait()
  except sail.SailboxCreationError:
      ...  # creation failed
  except sail.SailboxError:
      ...  # any other sailbox failure
  ```

  ```typescript TypeScript theme={null}
  import { Sailbox, SailboxCreationError, SailError } from "@sailresearch/sdk";

  try {
    const sb = await Sailbox.create({ app, name: "box" });
    await (await sb.exec("false", { timeoutSeconds: 5 })).wait();
  } catch (err) {
    if (err instanceof SailboxCreationError) {
      // creation failed
    } else if (err instanceof SailError) {
      // any other SDK failure
    }
  }
  ```

  ```rust Rust theme={null}
  use sail::{CreateSailboxRequest, SailError};

  match client
      .create_sailbox(
          &CreateSailboxRequest {
              app_id: app.id.clone(),
              name: "box".into(),
              ..Default::default()
          },
          /* timeout */ None,
      )
      .await
  {
      Ok(sb) => {
          let _ = sb.exec_shell("false", Default::default()).await?.wait().await?;
      }
      Err(SailError::Creation { message, .. }) => eprintln!("creation failed: {message}"),
      Err(err) => eprintln!("{err}"),
  }
  ```
</CodeGroup>

## Command failure is not an exception

A command that runs to completion and exits nonzero is not an SDK failure.
`run` and `exec(...).wait()` return normally with the exit code on the
result; check it yourself:

<CodeGroup>
  ```python Python theme={null}
  result = sb.run("make test")
  if result.exit_code != 0:
      print(result.stderr)
  ```

  ```typescript TypeScript theme={null}
  const result = await sb.run("make test");
  if (result.exitCode !== 0) {
    console.error(result.stderr);
  }
  ```

  ```rust Rust theme={null}
  let result = sb.run_shell("make test", RunOptions::default()).await?;
  if result.exit_code != 0 {
      eprint!("{}", result.stderr);
  }
  ```
</CodeGroup>

To treat command failure as an error, pass `check` to `run`: a nonzero exit
or a timeout then raises `CommandFailedError`, which carries the completed
result. Rust has no `check`; it reports a nonzero exit through the returned
`ExecResult`, so use the exit-code check above.

<CodeGroup>
  ```python Python theme={null}
  try:
      sb.run("make test", check=True)
  except sail.CommandFailedError as err:
      print(err.result.stderr)
  ```

  ```typescript TypeScript theme={null}
  try {
    await sb.run("make test", { check: true });
  } catch (err) {
    if (err instanceof CommandFailedError) console.error(err.result.stderr);
  }
  ```
</CodeGroup>

Exec errors in the taxonomy below cover the SDK failing to run the command at
all, not the command's own exit status.

## The taxonomy

| Failure                     | Python                            | TypeScript                        | Rust `SailError::`    |
| --------------------------- | --------------------------------- | --------------------------------- | --------------------- |
| Creation failed             | `SailboxCreationError`            | `SailboxCreationError`            | `Creation`            |
| Custom image build failed   | `ImageBuildError`                 | `ImageBuildError`                 | `ImageBuild`          |
| Exec failed                 | `SailboxExecutionError`           | `SailboxExecutionError`           | `Execution`           |
| Sailbox gone (terminated)   | `SailboxTerminatedError`          | `SailboxTerminatedError`          | `Terminated`          |
| Unknown exec request        | `SailboxExecRequestNotFoundError` | `SailboxExecRequestNotFoundError` | `ExecRequestNotFound` |
| Host machine lost mid-run   | `SailboxHostLostError`            | `SailboxHostLostError`            | `HostLost`            |
| Unknown id / unexposed port | `NotFoundError`                   | `NotFoundError`                   | `NotFound`            |
| Auth failure                | `PermissionDeniedError`           | `PermissionDeniedError`           | `PermissionDenied`    |
| Invalid argument            | `InvalidArgumentError`            | `InvalidArgumentError`            | `InvalidArgument`     |
| Missing guest file          | `FileNotFoundError`               | `FileNotFoundError`               | `FileNotFound`        |
| Readiness or build timeout  | `TimeoutError`                    | `TimeoutError`                    | `Transport` (timeout) |
| Writing to a closed stdin   | `BrokenPipeError`                 | `BrokenPipeError`                 | `BrokenPipe`          |
| Network/transport failure   | `TransportError`                  | `TransportError`                  | `Transport`           |
| Unexpected API response     | `ApiError`                        | `ApiError`                        | `Api`                 |

Every class derives from `sail.SailError`. The Python classes that match a
Python builtin also inherit it (`NotFoundError` is a `LookupError`,
`TimeoutError` the builtin `TimeoutError`, `ApiError` a `RuntimeError`, and
so on), so handlers written against the builtins keep working. API and
creation failures carry `status_code` (`status` in TypeScript) and the parsed
response `body`. Every error carries a `retryable` flag: `True` means
retrying the same call may succeed (the failure was transient, like a
network drop or a busy service), `False` means it is deterministic and a
retry would just fail the same way.

## Notable errors

### Creation failed

Raised when [`Sailbox.create`](/sailbox-sdk#sailbox-create) fails. When
creation succeeded but SSH setup failed (with `ssh=True`), the message carries
the new Sailbox's id so you can fetch it to retry `enable_ssh` or terminate
it.

### Host machine lost mid-run

The machine hosting your Sailbox failed before the command finished. The
command may have run only partially, and its output is gone. The run cannot be
resumed: calling [`exec`](/sailbox-sdk#exec) again starts it over from the
beginning, so any side effects the partial run applied will happen again. The
Sailbox itself recovers automatically; you do not need to resume it.

### Function errors (Python only)

`SailboxFunctionError` is raised when a
[`@sail.function`](/sailbox-sdk-images#sail-function) call fails while running
in the Sailbox. It carries the remote failure context:

| Attribute    | Type  | Description                  |
| ------------ | ----- | ---------------------------- |
| `error_type` | `str` | Remote exception class name. |
| `traceback`  | `str` | Remote traceback text.       |
| `stdout`     | `str` | Captured remote stdout.      |
| `stderr`     | `str` | Captured remote stderr.      |

`SailboxFunctionSerializationError` is raised when a function payload or
result cannot be serialized, or the remote function runtime cannot be prepared
(including a Python major.minor version mismatch between your local
interpreter and the Sailbox's `python3`). Both are subclasses of
`SailboxExecutionError`.
