Errors
Use this page to find the error class a failed call raises, what produced it, and which class to catch.
Both SDKs map every HTTP status and RPC code they recognise to a typed error.
Transport failures and a few internal invariants surface as the language's
plain Error / Exception, so keep a branch that rethrows anything you do
not recognise. The names are identical in the two languages except for the
suffix: Error in TypeScript, Exception in Python.
An error you think is a bug, or a call you wish existed? Report it with one POST, no key needed: see Send feedback.
Catch an error
Import the class you care about and catch it by type. SandboxError is the
base for most of them, so catch it last.
import { Sandbox, SandboxError, TimeoutError } from '@impello/sdk'
const sandbox = await Sandbox.create('base')
try {
await sandbox.commands.run('sleep 120', { timeoutMs: 5000 })
} catch (err) {
if (err instanceof TimeoutError) {
console.error('the command ran out of time', err.message)
} else if (err instanceof SandboxError) {
console.error('the sandbox refused the call', err.message)
} else {
throw err
}
} finally {
await sandbox.kill()
}The error classes
The table lists each class in both SDKs, its base class, and when it is thrown.
| TypeScript | Python | Extends | Thrown when |
|---|---|---|---|
SandboxError | SandboxException | Error | The base class, and the fallback for any status or RPC code with no mapping of its own |
TimeoutError | TimeoutException | SandboxError | The sandbox timed out, the request timed out, or a long-running call passed its own timeout |
InvalidArgumentError | InvalidArgumentException | SandboxError | The sandbox rejected an argument, or the SDK rejected one before sending it |
NotEnoughSpaceError | NotEnoughSpaceException | SandboxError | The sandbox's disk is full |
NotFoundError | NotFoundException | SandboxError | Deprecated. A resource was not found. Catch the two classes below instead |
FileNotFoundError | FileNotFoundException | NotFoundError | A file or directory inside the sandbox does not exist |
SandboxNotFoundError | SandboxNotFoundException | NotFoundError | The sandbox does not exist, or is no longer running |
RateLimitError | RateLimitException | SandboxError | You are being rate limited |
TemplateError | TemplateException | SandboxError | The template is too old for the call, or an alias or tag call was rejected |
GitUpstreamError | GitUpstreamException | SandboxError | git push or git pull ran on a branch with no upstream. Deprecated with the git module; run git through commands.run instead |
CommandExitError | CommandExitException | SandboxError | A command exited non-zero |
AuthenticationError | AuthenticationException | Error | The key is missing, malformed, or the server rejected it |
GitAuthError | GitAuthException | AuthenticationError | git clone, push or pull failed to authenticate with the remote. Deprecated with the git module; run git through commands.run instead |
BuildError | BuildException | Error | A template build failed. TypeScript only in practice |
FileUploadError | FileUploadException | BuildError | Uploading the build context failed. TypeScript only in practice |
VolumeError / VolumeException is exported too, but nothing throws it:
neither SDK ships a volume client. BuildException and FileUploadException
are exported by the Python SDK for parity in the same way. The Python SDK
covers sandboxes only and has no template builder, so nothing raises either of
them there; the template builder is TypeScript-only.
No error carries a retryable flag. RateLimitError is the only one whose
message tells you a retry is worth trying.
HTTP and RPC mapping
Three transports produce errors, and each has its own map.
The control plane
Calls to https://api.sandbox.impello.ai — create, connect, list, kill,
setTimeout, pause, snapshots, metrics, template builds.
| Status | Error |
|---|---|
401 | AuthenticationError. The message contains Unauthorized, please check your credentials. (prefixed with the status code in Python), and the server's own sentence is appended after a hyphen when it sends one |
429 | RateLimitError. The message contains Rate limit exceeded, please try again later (prefixed with the status code in Python), and the server's own sentence is appended after a hyphen when it sends one |
404 | SandboxNotFoundError from getInfo, getMetrics, setTimeout, updateNetwork, pause, createSnapshot, fork and connect |
| anything else | SandboxError, and its message is the status code, a colon and the server's own sentence. The template-build calls raise BuildError in its place, and the alias and tag calls raise TemplateError |
kill and deleteSnapshot are the exceptions: a 404 from either returns
false rather than throwing, because the thing you asked to delete is gone
either way.
The sandbox over HTTP
Filesystem reads, writes and uploads go to the sandbox itself.
| Status | Error |
|---|---|
400 | InvalidArgumentError |
401 | AuthenticationError |
404 | NotFoundError, and FileNotFoundError on filesystem calls |
429 | RateLimitError |
502 | TimeoutError — this is what a sandbox that has already timed out looks like |
507 | NotEnoughSpaceError |
| anything else | SandboxError |
The sandbox over RPC
Commands, the terminal and directory watches run over an RPC stream, so they carry status codes rather than HTTP statuses.
| Code | Error |
|---|---|
InvalidArgument | InvalidArgumentError |
Unauthenticated | AuthenticationError |
NotFound | NotFoundError, and FileNotFoundError on filesystem calls |
ResourceExhausted | RateLimitError |
Unavailable | TimeoutError — the sandbox timed out |
Canceled | TimeoutError — the request was cancelled in flight. In TypeScript the request timeout fired, or you aborted the signal you passed in. In Python the server or a proxy cancelled it, for example a sandbox that was paused or shut down |
DeadlineExceeded | TimeoutError — the call's own timeout was exceeded |
| anything else | SandboxError |
Three different failures therefore arrive as TimeoutError: the sandbox is
gone, your request timeout fired, or the call's own timeout fired. Read the
message — the TypeScript message names the option to change. In Python a
request timeout arrives as DeadlineExceeded over RPC, and as a
TimeoutException naming request_timeout on the HTTP calls. If the
connection is dropped mid-request, the SDK probes the sandbox's health. It
raises TimeoutError only once the probe confirms the sandbox is gone;
otherwise the transport error reaches you unchanged. See
Timeouts.
Commands and directory watches are the exception to the Unavailable row
above. An Unavailable on the first stream event raises
SandboxNotFoundError in TypeScript, with the message Sandbox is probably not running anymore. Catch that class as well as TimeoutError around
commands.run and files.watchDir. The Python SDK maps UNAVAILABLE to
TimeoutException in every case, so the two SDKs differ here. If the stream
closes before it sends its first event, both SDKs raise SandboxError
(SandboxException in Python) with the message The sandbox closed the stream before it sent a start event.
Command exits
commands.run waits for the process and throws when it exits non-zero. The
error carries the whole result, so you do not need to re-run anything to see
what happened.
import { Sandbox, CommandExitError } from '@impello/sdk'
const sandbox = await Sandbox.create('base')
try {
await sandbox.commands.run('cat /home/user/missing.txt')
} catch (err) {
if (err instanceof CommandExitError) {
console.error(err.exitCode, err.stderr, err.stdout)
}
} finally {
await sandbox.kill()
}CommandExitError exposes exitCode, error, stdout and stderr
(exit_code, error, stdout, stderr in Python). A background command
throws the same error from its handle's wait(). See
Run commands.
Authentication
The SDKs check the shape of your API key before any request leaves the
process. A key the SDK cannot recognise as a key at all raises
AuthenticationError immediately, without a round trip. The prefix is imp_
followed by hex characters. Whether the key is a good key is the server's
answer, and that arrives as 401.
The table lists each cause and the error it raises.
| Cause | Error |
|---|---|
No key in IMPELLO_API_KEY and none passed in | AuthenticationError, before any request |
Key is not imp_ plus hex | AuthenticationError, before any request |
| An older key, minted under the prefix Impello used before it moved to its own | Python raises AuthenticationException before any request, with a message that names the old prefix. TypeScript accepts the shape and the server answers 401. See Migrate |
| Key is well-formed but the server rejects it | AuthenticationError from the 401 |
API keys are created and read at
dashboard.impello.ai/keys. See
API keys. In TypeScript, set IMPELLO_VALIDATE_API_KEY=false
to skip the local shape check. The Python SDK always performs it, and its
validate_api_key option has no effect.
Refused creates
A create can be refused for reasons that have nothing to do with your code. No plan, no credit left, a spend ceiling you set on your own team, or a plan limit. The plan limits are how many sandboxes you may run at once, how fast you may start them, and how long a sandbox may live. A sandbox bigger than your plan allows — vCPU, memory or disk — is refused the same way. A create is also refused, never queued, when the fleet has no free capacity.
There is no dedicated error class for any of these, and they all surface the
same way. Read the message rather than branching on the type: the class you
get depends on the status the API returns. A 429 from any cause becomes
RateLimitError, and a status with no mapping of its own becomes
SandboxError, whose message is the status and the server's own sentence.
Top the team up or raise the ceiling at
dashboard.impello.ai/billing; the plan
limits themselves are on Limits and pricing.
Retrying does not help while the reason is a missing plan, your credit, your
ceiling or a plan limit. A team with no plan starts the free trial, if it
has not had one, or chooses a plan on the same page. Back off and retry a
capacity refusal or a
RateLimitError: both clear on their own. Impello publishes one address,
legal@impello.ai. Write there when a refusal is not
explained by your plan or your credit. Impello has no other support channel.
Old templates
TemplateError on a working call means the sandbox's agent is older than the
feature you asked for. Rebuild the template and the error goes away.
The table gives the agent version each call needs.
| Call | Needs |
|---|---|
| Creating a sandbox | 0.1.0 |
Recursive files.watchDir | 0.1.4 |
getMetrics | 0.1.5 |
metadata on files.write | 0.6.2 |
includeEntry on files.watchDir | 0.6.3 |
allowNetworkMounts on files.watchDir | 0.6.4 |
A sandbox created from a template older than 0.1.0 is killed for you before
the error is raised. See
Build a custom template.
Python names
Every class above that the Python SDK raises is exported from the top-level
impello package, with the same name and the same base class, ending in
Exception instead of Error:
from impello import (
AuthenticationException,
CommandExitException,
FileNotFoundException,
GitAuthException,
GitUpstreamException,
InvalidArgumentException,
NotEnoughSpaceException,
NotFoundException,
RateLimitException,
SandboxException,
SandboxNotFoundException,
TemplateException,
TimeoutException,
)
The async API raises exactly the same exceptions as the sync one.
Next: Limits and pricing.