## Summary
- `AsyncWatchHandle.stop()` and `AsyncCommandHandle.disconnect()`
previously only cancelled the consumer task and left the underlying
server-streaming gRPC call open — the `await self._events.aclose()` was
commented out as a Python 3.8 `RuntimeError` workaround. On long-lived
sandboxes this leaks one stream per call and eventually produces
`Code.internal: error creating watcher: too many open files`.
- The SDK now pins `python = "^3.10"`, so the workaround is removed.
`stop()`/`disconnect()` cancel the consumer task, await it, then
`aclose()` the async generator. The JS SDK already aborts the underlying
request via `AbortController`, so no JS change is needed.
## Test plan
- [ ] CI: `pnpm run format`, `pnpm run lint`, `pnpm run typecheck`
(passed locally)
- [ ] CI: `tests/async/sandbox_async/files/test_watch.py` and async
command tests still pass
- [ ] Reproduce the leak: in a long-lived async sandbox, repeatedly
create+stop a watcher and confirm fd count no longer climbs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
When `lifecycle.on_timeout` is set it wins; otherwise we fall back to
the `auto_pause` argument.
Previously the Python SDKs subscripted `lifecycle["on_timeout"]`, which
raised `KeyError` if a caller passed a `lifecycle` dict missing that key
(TypedDict is not enforced at runtime). The JS SDK silently used the
whole `lifecycle` object even when `onTimeout` was undefined. In both
cases, mixing `lifecycle` and `auto_pause` had inconsistent and
surprising behavior across the public surfaces (`create` vs
`beta_create`).
Now both SDKs use `.get`/optional chaining on `on_timeout` and only
treat `lifecycle` as authoritative when that field is actually present.
Touched files:
- `packages/python-sdk/e2b/sandbox_async/sandbox_api.py`
- `packages/python-sdk/e2b/sandbox_sync/sandbox_api.py`
- `packages/js-sdk/src/sandbox/sandboxApi.ts`
---------
Co-authored-by: Jakub Novak <jakub@e2b.dev>
## Summary
- Add optional `name` parameter to `createSnapshot` / `create_snapshot`
in the JS and Python SDKs so callers can name the resulting snapshot
template.
- Return the `names` field from the snapshot API on `SnapshotInfo` (both
in `createSnapshot` responses and in `listSnapshots` paginator results)
so callers can discover the namespaced snapshot names.
- Includes a changeset (`patch` for `e2b` and `@e2b/python-sdk`).
## Test plan
- [ ] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck` all pass
locally
- [ ] Integration tests on a sandbox with valid credentials:
`sandbox.createSnapshot({ name: 'my-snap' })` returns non-empty `names`
Resolves https://github.com/e2b-dev/E2B/issues/1249
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- Replace the sequential `for sandbox in sandboxes: await
sandbox.kill()` teardown in `async_sandbox_factory` with
`asyncio.gather(..., return_exceptions=True)` so a failure on one kill
no longer blocks cleanup of the rest, and teardown runs concurrently.
- Hoist the `_test_failed` check above the loop so the failure log is
printed once per fixture rather than per sandbox.
## Test plan
- [ ] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck`
(python-sdk) all pass
- [ ] `pnpm run test` against affected python async sandbox tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Resolves all 31 open [Dependabot
alerts](https://github.com/e2b-dev/e2b/security/dependabot) across the
workspace.
- **npm** — added range-based `pnpm.overrides` to bump vulnerable
transitive deps to their patched versions: postcss, vite, lodash,
brace-expansion, picomatch (2.x + 4.x), yaml, @tootallnate/once,
smol-toml, flatted, and minimatch (3.x/5.x/9.x/10.x).
- **python-sdk** — bumped dev deps in `poetry.lock`: pytest 7.4 → 9.0.3
(with constraint update in `pyproject.toml`), pytest-asyncio 0.23 → 1.3
(required for pytest 9), python-dotenv 1.2.2, pygments 2.20.0, requests
2.33.1, black 26.3.1; removed 4 now-unused `# ty: ignore` directives
that pytest 9's stricter type signatures made obsolete.
## Test plan
- [x] \`pnpm run typecheck\` passes
- [x] \`pnpm run lint\` passes
- [x] \`pnpm run format\` clean
- [x] CLI tests (80/80) and js-sdk/python-sdk unit tests pass;
integration tests not run locally (need \`E2B_API_KEY\`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Consolidates the fix and tests from #1285 and #1293 into a single PR.
- **js-sdk**: `uploadFile` used to pass a Node `Readable` directly to
`fetch`, causing undici to fall back to `Transfer-Encoding: chunked`. S3
presigned PUT URLs reject chunked with 501 NotImplemented. Fix buffers
the archive first so `Content-Length` is set. Includes:
- Regression test that spins up a local HTTP server and asserts
`Content-Length` is set and matches the body, and `Transfer-Encoding` is
not chunked.
- Type-fix for the CLI's typecheck (cast `Pack` →
`AsyncIterable<Buffer>`).
- Dynamic import of `node:stream/consumers` so the browser bundle
doesn't pull it in.
- **python-sdk**: Adds sync + async regression tests for `upload_file`
that guard against the same class of bug (someone swapping
`tar_buffer.getvalue()` for a stream/generator). No Python code change —
the current implementation already passes bytes to `httpx.put(...,
content=...)`.
Authorship of the original JS fix commit preserved (truffle-dev).
Closes#1243.
## Test plan
- [x] `pnpm run test tests/template/uploadFile.test.ts` — passes
- [x] `pnpm run typecheck` / `lint` clean across js-sdk and cli
- [x] `poetry run pytest tests/sync/template_sync/test_upload_file.py
tests/async/template_async/test_upload_file.py -v` — both pass
- [x] `poetry run make format` / `make lint` / `make typecheck` clean
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: truffle <truffleagent@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- Deletes install test files for apt, bun, npm, and pip in both JS and
Python SDKs
- Removes sync and async variants in Python
- Stacktrace tests for these install methods are kept
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Bumps metrics polling from 15s to 30s in Python async/sync and JS SDK
tests so the backend has enough headroom to populate metrics under load
— this test was the new #1 CI offender (5/9 Python runs and 4/10 JS runs
failed).
- Raises the async Python sandbox timeout from 20s to 60s for parity
with sync, and adds per-test timeout overrides
(`@pytest.mark.timeout(60)` / `{ timeout: 60_000 }`) so polling can
complete under the default 30s pytest/vitest cap.
- Happy path is unchanged: the loop still breaks as soon as metrics
appear.
## Test plan
- [x] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck` pass
- [x] `test_sbx_metrics` (Python async) passed locally in 8.4s
- [x] `test_sbx_metrics` (Python sync) passed locally in 15.6s
- [x] `metrics.test.ts` (JS) passed locally in 20.7s
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Fixes#1154
When creating a sandbox with an `mcp` config, the JSON-serialized config
is interpolated directly into a shell command wrapped in single quotes.
Since `json.dumps()` / `JSON.stringify()` do not escape single quotes,
any MCP config value containing a single quote (e.g., API keys, tokens,
URLs) breaks out of shell quoting and allows arbitrary command execution
inside the sandbox.
## Changes
### Python SDK (`sandbox_async/main.py`, `sandbox_sync/main.py`)
- Use `shlex.quote()` to properly escape the JSON config string (4
locations)
- `shlex.quote()` is a stdlib function designed exactly for this purpose
### JS/TS SDK (`sandbox/index.ts`)
- Add a `shellQuote()` helper that escapes single quotes using the
standard `'\'''` pattern (equivalent to Python's `shlex.quote()`)
- Apply it to both MCP config interpolation sites (2 locations)
## Before / After
**Before** (vulnerable):
```
mcp-gateway --config '{"servers": {"test": {"envs": {"KEY": "it's a value"}}}}'
# ^^ breaks out
```
**After** (safe):
```
mcp-gateway --config '{"servers": {"test": {"envs": {"KEY": "it'\''s a value"}}}}'
# ^^^^ properly escaped
```
## Testing
Verified escaping behavior for both Python (`shlex.quote`) and JS
(`shellQuote`) with the PoC from the issue — single quotes in config
values are properly escaped and no longer allow shell breakout.
---------
Co-authored-by: Mish Ushakov <10400064+mishushakov@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
- fix typos in hand-written docs and comments
- rename typoed helper variables in the CLI
- fix typoed test identifiers and descriptions in the JS SDK tests
- fix typoed credential warning text in the Python SDK
## Testing
- not run
Closes#1281
## Summary
- Fixes the Python SDK `write_files` docstring (both sync and async)
which incorrectly stated that writing to a non-existing directory would
produce an error
- The backend actually auto-creates parent directories, consistent with
the `write()` docstring and existing tests
(`test_write_to_non_existing_directory`)
## Test plan
- [x] Verified behavior with a test script — both `write()` and
`write_files()` auto-create nested directories
- [x] Existing tests pass (`test_write_to_non_existing_directory`,
`writeFiles creates parent directories`)
## Summary
- Adds optional `gzip` parameter to sandbox file read/write operations
across JS and Python SDKs
- Uploads are gzip-compressed via `CompressionStream` (JS) /
`gzip.compress` (Python) when enabled, downloads request
`Accept-Encoding: gzip`
- Only applies to the octet-stream upload path (envd >= 0.5.7), so older
envd versions are unaffected
- Includes tests for both SDKs covering write+read with gzip, write gzip
+ read plain, multi-file writes, and byte format reads
## Test plan
- [ ] Run JS SDK content encoding tests (`contentEncoding.test.ts`)
- [ ] Run Python async/sync content encoding tests
(`test_content_encoding.py`)
- [ ] Integration test with envd backend supporting `Content-Encoding:
gzip`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Switches sandbox filesystem file uploads from `multipart/form-data` to
`application/octet-stream` in both the JS and Python SDKs
- Each file is now uploaded as raw binary with the path passed as a
query parameter, matching the `application/octet-stream` content type in
the envd API spec
- Multi-file writes send one request per file sequentially
## Test plan
- [ ] Run JS SDK filesystem write tests (`pnpm run test` in
`packages/js-sdk`)
- [ ] Run Python SDK filesystem write tests (`pytest` in
`packages/python-sdk`)
- [ ] Verify single file write, multi-file write, and various data types
(string, bytes, streams)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Increases the default timeout for volume `writeFile`/`write_file`
operations from 60 seconds to 1 hour in both the JS and Python SDKs.
Other volume operations retain the existing 60s default. Users can still
override via `requestTimeoutMs` (JS) or `request_timeout` (Python).
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Made `runCode()` and `run_code()` references in READMEs link to the
[code interpreting docs](https://e2b.dev/docs/code-interpreting)
- Updated root README, js-sdk README, and python-sdk README
## Test plan
- [ ] Verify links render correctly on GitHub
- [ ] Confirm docs URL resolves
## Summary
- Updated root README, js-sdk README, and python-sdk README to show base
`e2b` SDK install and usage as the default
- Code-interpreter is now shown as an optional step for when
`runCode()`/`run_code()` is actually needed
- SDK links in descriptions now point to base `e2b` packages on npm/PyPI
## Why
The base `e2b` package covers commands, files, git, networking, and
sandbox lifecycle. Users who don't need code execution shouldn't be
directed to install `@e2b/code-interpreter` / `e2b-code-interpreter` as
their first step.
## Test plan
- [ ] Verify README renders correctly on GitHub
- [ ] Confirm base SDK examples use correct import syntax
- [ ] Confirm code-interpreter section still shows correct usage for
`runCode()`
extracts the `allow_internet_access`, `lifecycle`, and `network` configs
to the get info responses from the api when present.
Create a sandbox with lifecycle and network rules, check info while
running, pause it, and check info again. Network rules, lifecycle
config, and `allowInternetAccess` all returned while running and paused
```
$ e2b sandbox info xxx --format json
# running
{
"sandboxId": "xxx",
"templateId": "xxx",
"name": "stdin",
"metadata": {},
"allowInternetAccess": true,
"envdVersion": "0.4.3",
"startedAt": "2026-03-19T01:39:56.238Z",
"endAt": "2026-03-19T01:44:56.238Z",
"state": "running",
"cpuCount": 2,
"memoryMB": 1024,
"network": {
"allowOut": ["api.example.com", "cdn.example.com"],
"denyOut": ["0.0.0.0/0"],
"allowPublicTraffic": true
},
"lifecycle": {
"onTimeout": "pause",
"autoResume": true
}
}
# paused
{
"sandboxId": "xxx",
"templateId": "xxx",
"metadata": {},
"allowInternetAccess": true,
"envdVersion": "0.4.3",
"startedAt": "2026-03-19T01:39:56.238Z",
"endAt": "2026-03-19T01:40:27.964Z",
"state": "paused",
"cpuCount": 2,
"memoryMB": 1024,
"network": {
"allowOut": ["api.example.com", "cdn.example.com"],
"denyOut": ["0.0.0.0/0"],
"allowPublicTraffic": true
},
"lifecycle": {
"onTimeout": "pause",
"autoResume": true
}
}
```
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Updates the public `sandbox info` response shape across OpenAPI, JS,
and Python SDKs, which may impact downstream consumers that assume the
previous schema. Risk is moderate since changes are additive/optional
but touch generated models and response mapping logic.
>
> **Overview**
> **Sandbox info responses now include network and lifecycle
configuration when present.** The OpenAPI spec and generated JS schema
extend `SandboxDetail` with `allowInternetAccess`, `network`, and a new
`lifecycle` object (with `SandboxOnTimeout` and `SandboxLifecycle`).
>
> The JS SDK updates `SandboxApi.getFullInfo()` and exported types to
return these fields, introducing `SandboxInfoLifecycle` for info
responses. The Python SDK updates generated client models accordingly,
adds `SandboxLifecycle`/`SandboxOnTimeout` models, and maps
`SandboxDetail.network`/`SandboxDetail.lifecycle` into `SandboxInfo`
(plus exports `SandboxInfoLifecycle`).
>
> A changeset bumps `@e2b/python-sdk` and `e2b` as minor for the
expanded info payload.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
adb22292c08b1db9c8fe60c702f83fee2695af97. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes how instance methods merge and forward connection options (api
key/domain/headers/timeouts) to API calls in both JS and Python SDKs,
which can affect request routing and auth headers. Regression tests
reduce risk but behavior changes could impact callers relying on
previous (incorrect) defaults.
>
> **Overview**
> Fixes **missing propagation of instance `connectionConfig`** when
calling sandbox instance methods (notably `pause`/`betaPause`/`connect`,
plus related methods) so default config is always forwarded and per-call
overrides still win.
>
> In the JS SDK this centralizes option merging via a new
`resolveApiOpts()` helper and updates multiple `SandboxApi.*` calls to
use it; in the Python SDK it updates `Sandbox.connect()` and
`Sandbox.pause()` (sync + async) to pass
`self.connection_config.get_api_params(**opts)`.
>
> Adds regression tests in both SDKs to assert defaults are forwarded
and overrides are applied correctly.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
60aa6ca1c386d99613976269106298267d5dbfbe. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes public error/exception types in both JS and Python SDKs by
introducing new subclasses and remapping 404/NotFound conditions, which
may affect downstream error handling despite deprecation shims.
>
> **Overview**
> **Distinguishes “sandbox not found” from “file/directory not found”
across the SDKs.** Adds `FileNotFound*` and `SandboxNotFound*`
error/exception types (with `NotFound*` marked deprecated) and updates
sandbox lifecycle APIs to throw `SandboxNotFound*` for
missing/non-running sandboxes.
>
> Refactors envd HTTP/RPC error handling in both JS and Python to
support overridable status/code→error maps, and wires filesystem
operations to map 404/`NotFound` into `FileNotFound*`. Tests are updated
accordingly, and patch changesets are added for both packages.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
73da92694c02f71355b1f8625845c82865bf3b1d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Added `fixMissing` option to `aptInstall()` in JS SDK
- Added `fix_missing` parameter to `apt_install()` in Python SDK
- Enables `--fix-missing` flag for `apt-get install` command
🤖 Generated with Claude Code
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Low risk: adds an optional flag passthrough to the generated `apt-get
install` command in both SDKs, with no behavior change unless explicitly
enabled.
>
> **Overview**
> Adds an optional `fixMissing` (JS) / `fix_missing` (Python) parameter
to template `apt` install helpers so callers can emit `apt-get install
--fix-missing` when builds hit transient package download issues.
>
> Updates the JS type definitions/docs accordingly and includes a
changeset bumping `e2b` and `@e2b/python-sdk` as a minor release.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4c0b897e192c3ec6b880ab4e3f1695f1b1289d7b. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Changes request timeout semantics for server-stream calls by adding
`write` and conditional `read` timeouts, which could alter behavior for
long-running/slow streams but is limited to client-side networking
configuration.
>
> **Overview**
> **Improves timeout handling for server-stream requests in the Python
SDK.** `Client._prepare_server_stream_request` now builds a richer
`httpcore` timeout extension: `request_timeout` applies to `connect`,
`pool`, and `write`, and the separate `timeout` parameter is mapped to a
`read` timeout (to help prevent hanging streams).
>
> Adds a changeset to publish a patch release documenting the updated
`request_timeout` behavior.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
e66ef8e5056cfdb81892222881519e5e63f9e514. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Matt Brockman <matt.brockman@e2b.dev>
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Medium risk because it deletes a large subtree (`apps/web`) and
removes SDK-reference generation/commit steps from the package publish
workflow, which may affect downstream docs/release expectations.
>
> **Overview**
> **Removes the docs web app and generated SDK reference content.** The
PR deletes `apps/web` configs/scripts (Next.js/MDX setup, Sentry config,
prebuild/sitemap generation) and removes the committed `sdk-reference`
MDX pages.
>
> **Simplifies repo automation and ownership.** The package publish
workflow no longer generates/clones/commits SDK reference docs,
`CODEOWNERS` drops web/docs ownership entries, and the root ESLint
config removes `@stylistic/ts` in favor of the built-in `semi` rule.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
4158d777b5f3d3fa30b538e434d34ce0e697d473. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Enable glob patterns to match files starting with dot (e.g., `.env`,
`.gitignore`)
- JS SDK: Add `dot: true` to glob calls in `getAllFilesInPath`
- Python SDK: Add `glob.DOTMATCH` flag to glob calls in
`get_all_files_in_path`
- Add comprehensive tests for dotfile handling in both SDKs
Previously, the glob library defaults prevented dotfiles from being
matched, preventing upload of configuration files like `.env`. This fix
enables proper handling of dotfiles in template file uploads.
## Test plan
- ✅ All 16 JS SDK tests pass (4 new dotfile tests)
- ✅ All 17 Python SDK tests pass (4 new dotfile tests)
- ✅ `pnpm run format` passes
- ✅ `pnpm run lint` passes
- ✅ `pnpm run typecheck` passes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Small, well-scoped change to glob options that only broadens matched
file sets; main risk is unintentionally including hidden files unless
excluded via ignore patterns.
>
> **Overview**
> Template file collection now includes dot-prefixed files and
directories (e.g., `.env`, `.gitignore`, `.hidden/**`) when
building/uploading templates.
>
> This updates globbing in the JS SDK’s `getAllFilesInPath` to set `dot:
true` (including recursive directory expansion) and the Python SDK’s
`get_all_files_in_path` to add `glob.DOTMATCH`, and adds targeted tests
in both SDKs to verify dotfile inclusion and that ignore patterns still
exclude specified dotfiles. A changeset bumps both `e2b` and
`@e2b/python-sdk` as patch releases.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fc3cbcc232bc28559d38bb267162d3f55138b558. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>