Errors
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.
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
| 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 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.
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 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 current prefix is imp_ followed by
hex characters. Whether the key is a good key is the server's answer, and
that arrives as 401.
| 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 |
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 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 there is no capacity free right now.
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 your credit, your ceiling or a plan
limit. 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; there is no other support channel yet.
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.
| 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.