Skip to main content
LangGraph is LangChain’s runtime for building agents as explicit graphs — you write the nodes, the edges and the state. With E2B, code execution becomes a tool your graph calls: model-generated Python or shell commands run in an isolated sandbox, and the results flow back into graph state. This page is for graphs you build yourself. If you want a ready-made harness where E2B is the agent’s whole environment, use Deep Agents; for a deployable coding agent, use Open SWE. The comparison at the end spells out the difference.

Install the dependencies

Set API keys for E2B and your model provider:

A code-execution tool

The smallest useful integration is a tool that runs Python in a sandbox, given to a tool-calling agent. The sandbox is not created inside the tool — it is passed in through LangGraph’s runtime context, so every call in the run reuses the same kernel and the same filesystem:
runtime: ToolRuntime[Context] is injected by LangGraph and never appears in the schema the model sees — the model only sees code. You pass context= on every invoke, which is what makes the lifecycle below possible. Any tool-calling chat model works here; for OpenAI reasoning models use the Responses API (use_responses_api=True) and set reasoning={"effort": ...} — OpenAI recommends Responses for function calling with reasoning models.

Sandbox lifecycle across graph steps

A LangGraph run can outlive a single tool call by a lot: nodes run one after another, a checkpointer persists state between them, and interrupt() can hold the graph for as long as a human takes to answer. The sandbox has its own lifecycle. Three rules keep the two in step.

Keep the Sandbox in context and the sandbox_id in state

Graph state is checkpointed, so everything in it must be serialisable. The Sandbox object is not — it holds a live connection. Put the object in context_schema and its id in state:
Every node in the run reads runtime.context.sandbox and gets the same sandbox. Files written in one node are there for the next; variables defined in one run_code call are defined in the next.

Rebuild the context when you resume

Context is not checkpointed. After an interrupt(), or when a resume runs in a different process from the original invoke, nodes see runtime.context as None. The only thing the checkpoint kept is the sandbox_id in state — reconnect from it:
Sandbox.connect() returns a handle to the same sandbox, with its filesystem and running processes intact. If the sandbox was paused in the meantime, connect() resumes it.

Let the sandbox outlive the interrupt

The default sandbox timeout is 300 seconds. A human reviewing a plan may take longer, and a sandbox that hit its timeout is gone. Tell E2B to pause instead of kill, and to wake up when the graph comes back:
On timeout the sandbox is paused with its memory and filesystem preserved, and it stays paused until you kill it. The next SDK call — Sandbox.connect(), run_code(), files.read() — resumes it automatically. See Auto-resume and Persistence.

Tear down explicitly

Kill the sandbox in a finally block so cleanup runs whether the graph finished, was rejected at the interrupt, or raised. If you would rather tie it to the agent’s lifetime, an AgentMiddleware after_agent hook receives the same runtime and can call runtime.context.sandbox.kill().

Plan-and-execute coding agent

The cookbook example puts all of this into one hand-wired graph. It repairs a small Python project whose tests fail:
  • bootstrap uploads the project, installs pytest and records the failing output — in a sandbox created with on_timeout: pause.
  • plan asks the model for an ordered list of file changes as structured output.
  • approve stops the graph with interrupt(); the resume reconnects from sandbox_id.
  • execute runs one plan step per node visit with a small tool-calling agent (read_file, write_file, run_command), always in the same sandbox — pytest installed in bootstrap is still there.
  • verify re-runs the suite; the graph re-plans on red, at most three times, and exits non-zero if the tests never pass.
The example ships with unit tests that run the graph against a fake sandbox, so the resume and re-plan paths are checked without a network.

Results and errors

run_code() returns an Execution:
  • logs.stdout / logs.stderr — lists of output lines;
  • results — rich outputs such as text, charts (png), HTML or JSON; execution.text is the text of the last one;
  • error — None, or an object with name, value and traceback when the cell raised.
Return the error to the model and tell it to check for one before answering; a wrong answer with an unread traceback is the most common failure mode. For shell commands, sandbox.commands.run() raises CommandExitException on a non-zero exit — it carries exit_code, stdout and stderr, so a tool can catch it and return all three.

Security considerations

E2B isolates model-generated code from the machine running your graph. The sandbox still processes untrusted input and produces untrusted output:
  • do not expose API keys or other host secrets to the sandbox unless the task needs them;
  • keep sandbox and per-call timeouts as short as the workload allows, and use on_timeout: pause rather than a very long timeout when a human is in the loop;
  • kill sandboxes you own, in finally;
  • treat files the agent writes as a proposal to review, not a result to trust.

LangGraph vs. Deep Agents vs. Open SWE

All three are LangChain projects and all three run on the LangGraph runtime. They differ in how much graph you write and in what role the sandbox plays:

Sandbox persistence

Pause and resume sandboxes with memory and filesystem intact

Auto-resume

Pause on timeout and wake up on the next request

Deep Agents

LangChain’s agent harness with E2B as the sandbox backend