Skip to content

Throw descriptive error when sandbox is killed mid-request#291

Open
mishushakov wants to merge 3 commits into
mainfrom
mishushakov/handle-sandbox-killed-econnreset
Open

Throw descriptive error when sandbox is killed mid-request#291
mishushakov wants to merge 3 commits into
mainfrom
mishushakov/handle-sandbox-killed-econnreset

Conversation

@mishushakov

Copy link
Copy Markdown
Member

When the sandbox is killed or times out while a request is in flight, runCode/run_code and the context-management methods surfaced a raw socket error (e.g. ECONNRESET on Bun, TypeError: fetch failed on Node, httpx.ReadError/RemoteProtocolError in Python). Both SDKs now detect the closed connection across runtimes, confirm via the sandbox health check that it's actually gone, and throw a descriptive SandboxError/SandboxException suggesting timeoutMs/.setTimeout instead. If the sandbox is still running or its state can't be determined, the original error propagates unchanged so genuine network issues aren't masked. Includes kill-during-execution tests for JS, Python sync, and Python async (verified against the live API on Node and Bun), plus a patch changeset for both packages.

🤖 Generated with Claude Code

When the sandbox is killed or times out while a request to the Jupyter
server is in flight (runCode/run_code or context management), the SDKs
surfaced a raw socket error (e.g. ECONNRESET). Now they detect the
closed connection, confirm the sandbox is gone via its health check,
and throw a descriptive SandboxError/SandboxException instead. If the
sandbox is still running (or its state can't be determined), the
original error propagates unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes error handling on all Jupyter HTTP paths and adds an extra isRunning() call on connection failures, which could affect failure modes or latency in edge cases but avoids masking real network errors when state is unknown.

Overview
When a sandbox dies or times out while Jupyter traffic is in flight, runCode/run_code and code-context APIs no longer surface raw connection failures (ECONNRESET, fetch failed, httpx.ReadError, etc.). Both SDKs now treat those as “connection closed,” confirm the sandbox is not running via isRunning(), and raise a TimeoutError/TimeoutException that explains the kill/timeout and points users to timeoutMs/.setTimeout (or Python timeout/.set_timeout).

On JavaScript, isConnectionClosedError normalizes runtime-specific socket errors (including nested cause on Node/undici). throwIfSandboxKilled runs in the outer catch paths for runCode and context create/remove/list/restart; if the health check fails or is inconclusive, the original error is preserved so unrelated network issues are not mislabeled.

On Python (sync and async), httpx.ReadError and RemoteProtocolError trigger the same _raise_if_sandbox_killed path via format_sandbox_killed_error(). Integration tests kill the sandbox mid-run_code; a patch changeset covers @e2b/code-interpreter and @e2b/code-interpreter-python.

Reviewed by Cursor Bugbot for commit 17cce6a. Bugbot is set up for automated code reviews on this repo. Configure here.

mishushakov and others added 2 commits June 11, 2026 14:10
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Matches the existing 502 mapping in extractError/extract_exception and
the base SDK convention: a dead sandbox surfaces as TimeoutError /
TimeoutException. When the health probe is inconclusive or the sandbox
is still running, the original transport error propagates unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 17cce6a. Configure here.

sandboxTest.skipIf(isDebug)(
'runCode throws a descriptive error when the sandbox is killed during execution',
async ({ sandbox }) => {
const execution = sandbox.runCode('import time; time.sleep(60)')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kill test sleep too short

Medium Severity

The new kill-during-execution tests use time.sleep(60) while runCode/run_code rely on the default execution timeout (60s in JS, 300s in Python). That ratio is far below the project’s 10×/100× guidance for interrupt-style execution tests, so on JS the run can hit the default timeoutMs abort around the same time as the sleep ends instead of staying in-flight when the sandbox is killed.

Additional Locations (2)
Fix in Cursor Fix in Web

Triggered by learned rule: Test sleep durations must far exceed test timeouts

Reviewed by Cursor Bugbot for commit 17cce6a. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant