How to stream command output from a sandbox
Pass onStdout to exec (on_stdout in Python), or loop over execStream(); each chunk arrives as the command prints it.
On Runtime a streamed command has no output limit, and a long stream picks itself up again from the right byte. When a stream runs past the server's time limit, the SDK resumes it from a cursor, so no line is lost or printed twice. A command in a sandbox already in use answered in a median 105 ms, measured on 24 September 2026 (speed), so the first line of a build or a test run reaches your terminal almost at once.
Print output as it arrives
The callbacks get each piece of stdout and stderr as text, and the result
still holds everything the command printed:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();const result = await sbx.exec("for i in 1 2 3; do echo line $i; sleep 1; done", { onStdout: (text) => process.stdout.write(text), onStderr: (text) => process.stderr.write(text),});console.log("exit", result.exitCode, result.stdoutTruncated);Pythonimport sysfrom withruntime import Sandboxwith Sandbox.create() as sbx: result = sbx.exec("for i in 1 2 3; do echo line $i; sleep 1; done", on_stdout=sys.stdout.write, on_stderr=sys.stderr.write) print("exit", result.exit_code, result.stdout_truncated)From a terminal, runtime sandbox exec streams by default and exits with the
command's own exit code, 124 when it timed out, so set -e scripts behave:
Terminalid=$(runtime sandbox create)runtime sandbox exec "${id}" -- python3 -c 'print(6 * 7)'runtime sandbox stop "${id}"Read the events one by one
When you want to tell the streams apart or react to the exit, iterate the
events. Each has a type: start, stdout, stderr or exit.
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();for await (const event of sbx.execStream("npm --version")) { if (event.type === "stdout") process.stdout.write(event.data); if (event.type === "stderr") process.stderr.write(event.data); if (event.type === "exit") console.log("exit", event.exitCode);}Pythonfrom withruntime import Sandboxwith Sandbox.create() as sbx: for event in sbx.exec_stream("npm --version"): if event["type"] == "stdout": print(event["data"], end="") elif event["type"] == "exit": print("exit", event["exitCode"])Over plain HTTP, POST /v1/sandboxes/{id}:exec with "stream": true answers
NDJSON, one event a line, and a continue event carries the cursor to resume
from (commands and processes).
Output limits
| Case | What you get back |
|---|---|
exec with no callback, timeout up to 60s |
At most 64 KiB of stdout and 64 KiB of stderr in the result |
exec with onStdout or onStderr |
Streams; the result keeps everything the command printed |
exec with timeoutMs over 60,000 |
Streams by itself, as if you passed a callback |
execStream() |
Every event, with no output limit |
| A reader far behind | The sandbox keeps the latest 1 MiB; a truncated event says so |
| Timeout | 60 seconds by default, 24 hours at most; a timeout is a result |
runtime sandbox exec --json |
One result, at most 64 KiB of each stream, with the truncation flags |
Output costs nothing on its own. The sandbox is billed for the CPU it uses, $0.025 per active vCPU-hour, and its memory, $0.0075 per GiB-hour (pricing).
Mistakes to avoid
- Reading a big log from the plain result. Past 64 KiB the rest is dropped
and
stdoutTruncatedistrue. Check the flag, add a callback, or send the output to a file (cmd > /workspace/out.log) and read it withsbx.files.readText. - Treating a timeout as an exception. It comes back as
timedOut: true(timed_outin Python) with the output so far. Addcheck: trueto throwCommandErroron a non-zero exit instead. - A reader that stops reading. The sandbox holds the latest 1 MiB of a
command's output. A consumer that falls further behind loses the oldest part,
gets a
truncatedevent, and both truncation flags are set. Keep the loop reading, or write to a file. - Streaming a server.
execwaits for the command to end. A dev server or a watcher belongs in a background process, whose output you follow separately. - A secret in the command line. Pass it in
env: it is never echoed back, and journals record a hash, not the value.
Related
- Run a background process and follow its logs after you disconnect.
- Open an interactive terminal for colours and prompts.
- Run untrusted LLM code, where an agent reads output as it comes.
- The full exec reference in JavaScript and Python.
Facts on this page were checked on 25 September 2026.