run_code, your code runs inside that process’s shared globals dict — so anything you define in one call is immediately available in the next. This is the same execution model as a Jupyter kernel or the Anthropic code_execution_20260120 tool: state accumulates across calls, and you only pay the cost of imports and data loading once.
State persists across calls
run_code call sends a block of code to the same running Python process. You don’t need to re-import libraries or re-assign variables between calls.
What persists
- Named values —
x = 1,df = pd.read_csv(...) - Imported modules —
import pandas as pd,from pathlib import Path - Open file handles — files opened with
open()stay open across calls - Sockets and threads — long-lived I/O objects survive between calls
- Subprocesses started with
subprocess.Popen— usePopenif you need a persistent child process - Changes to
sys.path, environment variables, and CWD
What does not persist
- Shell commands — each bash
run_codecall launches a fresh subprocess. Shell variables likeX=foodo not carry over between calls. If you need persistent state, use Python. - Processes started with
subprocess.run— they run, exit, and are gone.
Building up state across multiple calls
This pattern is common for data analysis agents that load large datasets once and then explore them:Streaming output with run_code_stream
Userun_code_stream when you want to receive stdout and stderr as they are produced rather than waiting for the call to complete:
Exceptions don’t poison the REPL
A raised exception in one call does not crash or reset the sandbox. The REPL stays alive and the next call works normally:SystemExit is caught too — its exit code becomes the call’s exit_code, not a sandbox crash.
When to create a new sandbox vs reuse
| Situation | Recommendation |
|---|---|
| Continuing a multi-step analysis | Reuse the same sandbox |
| Starting a completely unrelated task | Create a new sandbox |
| Running the same code with different inputs | fork(n) from a preloaded parent |
| Recovering from a corrupted global state | Create a new sandbox |
REPL state is inherited by fork
When you callfork(n), every child starts from the parent’s exact Python process state — variables, imports, loaded data, and all. Because the fork uses a memory snapshot with copy-on-write semantics, a 1 GB DataFrame loaded in the parent costs near-zero additional memory per child until a child modifies it.
Related concepts
Fork
Branch a sandbox and inherit REPL state in every child
Sandboxes
Sandbox lifecycle and resource defaults