#1587·E2B

[Bug]: reattached stdout stream stalls after auto-resume (regression in python-sdk 2.29.1)

Author: nseniakCreated Jul 23, 2026Updated Sep 11, 2026
Labelsbugsdk

Summary

After a sandbox auto-pauses and resumes, reattaching to an existing long-lived background process via commands.connect(pid) produces a stdout stream that intermittently delivers its first response and then goes silent — the on_stdout callback stops firing for subsequent responses even though the process is still running and still writing.

This is a regression introduced in python-sdk 2.29.1 and present in every release since (through 2.34.0). The likely change is cb061d2 ("Fix several command and PTY streaming issues"), which reworked incremental stdout/stderr decoding and stream-connection teardown.

Bisect (strong evidence)

Identical workload, same account/templates; only the SDK version changed. The workload is a real stdio server (@modelcontextprotocol/server-everything over npx) driven through repeated pause → resume → commands.connect(pid) reattach cycles. "Stall rate" = fraction of reattach cycles where the reconnected stdout stream failed to deliver a complete response, over 6 cycles:

Version Stall rate
2.20.2 1/6
2.21.0 1/6
2.27.0 1/6
2.29.0 1/6 ← last good
2.29.1 6/6 ← first bad
2.30.0 6/6
2.34.0 6/6

The transition is sharp and clean: every version up to and including 2.29.0 stalls ~1/6; every version from 2.29.1 on stalls ~6/6. The only command/stream change in 2.29.1 is cb061d2.

Reproduction conditions

The stall reproduces reliably only through a realistic streaming workload — a long-lived process reattached after resume that answers several requests with sizeable, multi-chunk stdout responses (in our case JSON-RPC tools/list / resources/list / prompts/list from an stdio MCP server). The pattern observed: the first response after reattach arrives, later ones never do.

A trivial synthetic process (one small line per second, or a single large one-shot write) does not isolate the regression — those either stream fine on both versions, or hit an unrelated single-write truncation that is version-independent. So the reliable repro is the real stdio-server flow above; we can share the exact harness on request.

Expected vs actual

  • Expected (≤ 2.29.0): after reattach, on_stdout keeps firing for every subsequent response; a full multi-request refresh completes.
  • Actual (≥ 2.29.1): after reattach, the stream delivers the first response (sometimes partially) then goes silent; subsequent responses never arrive, while a fresh reconnect shows the process still alive.

Impact

Any workload relying on a long-lived background process surviving auto-pause and being reattached after resume (e.g. an stdio server behind a gateway) loses output on nearly every resume from 2.29.1 on, forcing a full fresh-sandbox rebuild as a workaround. On 2.29.0 this was rare (~1/6); from 2.29.1 it is effectively every resume.

Environment

  • python-sdk 2.29.1 first bad, confirmed through 2.34.0; 2.29.0 last good
  • AsyncSandbox.create(lifecycle={"on_timeout": "pause", "auto_resume": True})
  • Reattach via AsyncSandbox.connect(sandbox_id=...) + commands.connect(pid=...)
  • Suspected change: cb061d2 (incremental stdout/stderr decode + stream teardown)