DocsTimeouts

Timeouts

Three separate clocks apply to your code: how long the sandbox lives, how long a single command may run, and how long one API request may take.

The sandbox timeout

Every sandbox has a deadline. It is set when you create the sandbox and defaults to five minutes — 300_000 ms in TypeScript, 300 seconds in Python. When the deadline passes the sandbox is killed unless you asked for something else.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', { timeoutMs: 1_800_000 })

console.log(sandbox.sandboxId)

TypeScript counts milliseconds and Python counts seconds. The API takes whole seconds, so the TypeScript SDK rounds a millisecond value up to the next second before sending it.

Extend it while running

setTimeout replaces the remaining time with a new one, measured from the moment the call arrives. It can extend or shorten the life of the sandbox, and each call replaces the previous deadline. There is an instance method and a static one that takes a sandbox ID, so a process that never held the sandbox object can still keep it alive.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')

// Another fifteen minutes from now.
await sandbox.setTimeout(900_000)

// Or from anywhere, with only the ID.
await Sandbox.setTimeout(sandbox.sandboxId, 900_000)

Because each call restarts the count from the current time, a long job is usually a short timeout plus a heartbeat, not one enormous timeout.

To read the deadline rather than set it, ask the sandbox for its information. getInfo returns endAt in TypeScript and get_info returns end_at in Python, both as a date.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
const info = await sandbox.getInfo()

console.log(info.startedAt, info.endAt)

connect also takes a timeout, but it only ever extends: reconnecting to a running sandbox with a shorter timeout than the one it already has leaves the existing deadline alone. Use setTimeout when you mean to shorten it.

What happens at timeout

By default the sandbox is killed and its filesystem goes with it. Pass lifecycle at create time to have it pause instead, so you can resume it later from the same sandbox ID.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', {
  timeoutMs: 600_000,
  lifecycle: { onTimeout: { action: 'pause', keepMemory: true } },
})

console.log(sandbox.sandboxId)

onTimeout (on_timeout in Python) also accepts the bare string 'pause' or 'kill'. The object form exists to carry keepMemory (keep_memory), which chooses the kind of snapshot the automatic pause takes:

  • true keeps memory as well as the filesystem, so running processes and open connections are still there when you resume. TypeScript sends true when you omit it; Python sends nothing and the API chooses.
  • false keeps only the filesystem. Resuming cold-boots the sandbox from disk, and anything that was running is gone.

keepMemory is only meaningful with action: 'pause'. Passing it alongside action: 'kill' is a type error in both SDKs, and at runtime it raises InvalidArgumentError in TypeScript and InvalidArgumentException in Python.

A sandbox that has sat paused for 14 days is deleted. Pausing it again restarts the 14 days. See Pause, resume and snapshots.

Once a sandbox has reached the end of its life, a call into it — a command, a file read, a PTY write — fails with TimeoutError (TimeoutException). The message tells you to raise timeoutMs (timeout) at create time, or to call setTimeout (set_timeout). A control-plane call for a sandbox that no longer exists — getInfo, setTimeout, connect — raises SandboxNotFoundError (SandboxNotFoundException) instead. See Errors.

Command and request timeouts

The other two clocks are shorter and are set per call, not per sandbox.

ClockTypeScriptPythonDefault
Sandbox lifetimeoutMs on createtimeout on create300_000 ms / 300 s
One commandtimeoutMs on commands.runtimeout on commands.run60_000 ms / 60 s
One API requestrequestTimeoutMsrequest_timeout60_000 ms / 60 s

A command that runs past its own timeout raises TimeoutError / TimeoutException even though the sandbox is healthy and still running. Raise the command timeout, or start the command in the background and wait for it yourself. Passing 0timeoutMs: 0 in TypeScript, timeout=0 in Python — removes the limit on a command entirely.

import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', { timeoutMs: 900_000 })

const result = await sandbox.commands.run('sleep 120 && echo done', {
  timeoutMs: 300_000,
})

console.log(result.exitCode, result.stdout)

The error you catch then describes the sandbox, not the command. The SDK sees the connection drop mid-request and checks whether the sandbox is still alive. When it is gone, you get TimeoutError (TimeoutException) saying the sandbox was killed or reached its end of life while the request was in flight. If that check cannot reach the sandbox, Python raises the underlying transport error unchanged and TypeScript wraps it in SandboxError with the transport message.

requestTimeoutMs (request_timeout) bounds a single non-streaming request. That covers the API — creating a sandbox, listing sandboxes, setting a timeout — and the sandbox itself: reading or writing a file, listing or killing a command. Pass it on create and connect to set it for every later call on that sandbox, or on an individual call to override it there. Setting it to 0 disables it.

It does not bound a streaming call. On commands.run and the filesystem watchers, TypeScript uses it only for the handshake and Python ignores it entirely — bound those with the command timeout instead.

Maximum length by plan

The longest deadline you can ask for depends on your plan.

PlanMaximum sandbox length
Micro1 hour
Base12 hours
Scale24 hours

Keep every timeoutMs and setTimeout value inside your plan's maximum. Neither SDK checks the number before sending it. If the API refuses the request, create and setTimeout raise SandboxError (SandboxException) carrying the status code and the server's own sentence — the fallback described in Errors. Read endAt back from getInfo when the exact deadline matters. See Limits and pricing.

Async Python

AsyncSandbox has the same calls awaited: await AsyncSandbox.create("base", timeout=1800), await sandbox.set_timeout(900) and await sandbox.get_info(). It takes the same lifecycle argument.

Next: Errors.