Skip to content

feat(js-sdk)!: drop undici 7 and the in-flight cap, depend on undici@^8 only - #1733

Merged
mishushakov merged 3 commits into
mainfrom
devin/1787234613-drop-undici7
Oct 13, 2026
Merged

mishushakov merged 3 commits into
mainfrom
devin/1787234613-drop-undici7

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Follow-up to the discussion on #1717 (#1717 (comment)). This PR drops the dual undici/undici8 selection, depends on undici@^8 only, and removes the SDK-level in-flight request cap entirely (per #1733 (comment)). It supersedes #1717: with the cap gone, there is no slot accounting left to fix.

Why: undici 8 gates HTTP/2 dispatch on the server's SETTINGS_MAX_CONCURRENT_STREAMS (busy() is true at running >= maxConcurrentStreams). When a connection is saturated, the pool opens another one, and queues once the pool is full. It never throws ERR_HTTP2_TOO_MANY_CONCURRENT_STREAMS, so undici 7 and the SDK's own semaphore are both redundant. Main already requires Node 22; only Node 22.0–22.18 still needed undici 7.

export async function loadUndici(
  nodeVersion = globalThis.process?.versions?.node
): Promise<UndiciModule | undefined> {
  if (!nodeVersion || compareVersions(nodeVersion, UNDICI_MIN_NODE) < 0) {
    return undefined
  }

  try {
    return await dynamicImport<UndiciModule>('undici')
  } catch {
    return undefined
  }
}
  • packages/js-sdk/package.json: undici: ^8.11.2 is now a regular dependency, and the undici8 optional alias is removed.
  • loadUndici imports undici directly; the getUndiciPackageCandidates list and loop are removed. When Node is older than 22.19.0, or the import fails (e.g. in bundled apps), it returns undefined and buildDispatchedFetch falls back to the global fetch.
  • engines.node is now >=22.19.0, undici 8's own engine floor:
    • e2b, @e2b/code-interpreter and @e2b/desktop move up from >=22.
    • @e2b/cli moves up from >=22.9.0, so it no longer advertises Node versions its e2b dependency rejects.
  • The in-flight cap is removed:
    • src/api/inflight.ts (limitConcurrency) and parseInflightLimitEnv are deleted, and buildDispatchedFetch no longer takes inflightLimit.
    • The E2B_API_INFLIGHT_REQUESTS, E2B_ENVD_INFLIGHT_REQUESTS and E2B_ENVD_RPC_INFLIGHT_REQUESTS env vars are gone.
    • Concurrency is now governed by undici's dispatcher: the stream limit the server advertises per HTTP/2 connection, plus the E2B_API_CONNECTIONS / E2B_ENVD_RPC_CONNECTIONS pools.
  • Toolchain moves to the latest Node 22 (22.23.3):
    • codegen.Dockerfile goes from 22.18.0 to 22.23.3, matching .tool-versions. With 22.18.0, make generate-js failed the new engine floor because .npmrc sets engine-strict=true.
    • The @types/node catalog entry is now ^22.20.5.
  • Changeset: minor for e2b, @e2b/cli, @e2b/code-interpreter and @e2b/desktop, matching the pending Node 22 floor changeset. It marks the removed env vars as a breaking change.

Usage is unchanged:

import { Sandbox } from 'e2b' // now requires Node >= 22.19.0

Verified:

  • Locally on Node 24:
    • pnpm run format, lint and typecheck pass, and the js-sdk builds.
    • The tests/api, tests/envd and tests/undici.test.ts unit tests pass (152/152).
  • Live against api.e2b.dev with the built SDK (undici 8.11.2 resolved), all of these pass:
    • 20 parallel streaming commands.run
    • files.write/read
    • a background command plus kill()
    • Sandbox.list()
  • The "Generated files" CI job runs make generate in the updated codegen image and reports no diff.

Link to Devin session: https://app.devin.ai/sessions/dcc33af2c8d342c7b9d9981fb5969bb0
Open in Devin Desktop: https://app.devin.ai/desktop/session/dcc33af2c8d342c7b9d9981fb5969bb0?variant=devin
Requested by: @mishushakov

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@cla-bot cla-bot Bot added the cla-signed label Aug 20, 2026
@changeset-bot

changeset-bot Bot commented Aug 20, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 78371b2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
e2b Minor
@e2b/cli Minor
@e2b/code-interpreter Minor
@e2b/desktop Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Aug 20, 2026 •

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from e361fb8. Download artifacts from this workflow run.

JS SDK (e2b@2.55.1-devin-1787234613-drop-undici7.0, with @e2b/dockerfile-utils@0.1.1-devin-1787234613-drop-undici7.0):

npm install ./e2b-dockerfile-utils-0.1.1-devin-1787234613-drop-undici7.0.tgz ./e2b-2.55.1-devin-1787234613-drop-undici7.0.tgz

CLI (@e2b/cli@2.21.3-devin-1787234613-drop-undici7.0):

npm install ./e2b-cli-2.21.3-devin-1787234613-drop-undici7.0.tgz

Code Interpreter JS SDK (@e2b/code-interpreter@2.8.4-devin-1787234613-drop-undici7.0):

npm install ./e2b-code-interpreter-2.8.4-devin-1787234613-drop-undici7.0.tgz

Desktop JS SDK (@e2b/desktop@2.4.1-devin-1787234613-drop-undici7.0):

npm install ./e2b-desktop-2.4.1-devin-1787234613-drop-undici7.0.tgz

Python SDK (e2b==2.55.1+devin.1787234613.drop.undici7, with e2b-dockerfile-utils==0.1.0+devin.1787234613.drop.undici7):

pip install ./e2b_dockerfile_utils-0.1.0+devin.1787234613.drop.undici7-py3-none-any.whl ./e2b-2.55.1+devin.1787234613.drop.undici7-py3-none-any.whl

Code Interpreter Python SDK (e2b-code-interpreter==2.10.5+devin.1787234613.drop.undici7):

pip install ./e2b_code_interpreter-2.10.5+devin.1787234613.drop.undici7-py3-none-any.whl

Desktop Python SDK (e2b-desktop==2.6.1+devin.1787234613.drop.undici7):

pip install ./e2b_desktop-2.6.1+devin.1787234613.drop.undici7-py3-none-any.whl

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TASTE.md review — 0 public-surface violations, 3 correctness/consistency notes

Checked the rules that could bite a dependency/runtime-gating change: parity (T-1, T-2), option and naming shape (T-3, T-9, T-10, T-12, T-22, T-23), undefined over null (T-20), version gating via named constants (T-55), version never hardcoded (T-56), flat entry point (T-54), error actionability (T-62, T-63), and comments/docs (T-69, T-70). The PR changes no public API surface, keeps the Node floor in a named constant (UNDICI_MIN_NODE, T-55-style), and the changeset correctly marks the engines bump as major. No TASTE violation to anchor inline.

The notes below are correctness/consistency, not TASTE. Two of them are on lines the diff doesn't touch:

  1. Stale comment in packages/js-sdk/src/template/buildApi.ts (~line 183–184) — it still says "loadUndici tries undici 8 first and falls back to undici 7 where 8 doesn't import (Bun)", which this PR makes false.
  2. Leftover pnpm override in root package.json (line 60) — "undici@>=7.0.0 <7.29.0": "^7.29.0" pins a major that is no longer a direct dependency. Harmless if a transitive dep still pulls undici 7, dead config otherwise; worth deciding explicitly rather than leaving behind.
  3. Bun: see the inline comment on getUndiciPackageCandidates.

Comment thread packages/js-sdk/src/undici.ts Outdated
Comment on lines +26 to +30
if (compareVersions(nodeVersion, UNDICI_MIN_NODE) >= 0) {
return ['undici']
}

return ['undici']
return []

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not a TASTE rule, a behavior regression the diff makes easy to miss. buildApi.ts documents that undici 8 doesn't import on Bun, which is exactly why undici (7) was the trailing candidate. With undici 7 gone and the gate returning [] below 22.19.0, loadUndici() resolves to undefined on any runtime whose process.versions.node is below the gate (Bun's emulated version) — so uploadFile loses the undici fetch it prefers for honoring an explicit Content-Length on stream bodies. Worth confirming against test:bun before merging, and either way the changeset should mention Bun, since it only mentions Node.

Also, now that the list can hold at most one entry, the plural ...Candidates name and the loop in loadUndici are vestigial — a supportsUndici(nodeVersion): boolean (or returning 'undici' | undefined) says what is actually being decided.

Comment thread packages/js-sdk/src/undici.ts Outdated
return await dynamicImport<UndiciModule>(packageName)
} catch {
// Try the next package supported by this Node version.
// Fall back to the capped global fetch when undici cannot load.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

T-69 (comments/docs are part of the surface and should state the failure mode where it happens): this catch doesn't fall back to anything — it continues the loop, and the capped-global-fetch fallback lives in buildDispatchedFetch. Describing the caller's behavior here misplaces it; the local truth is just that the import failed and undefined is returned.

Suggested change
// Fall back to the capped global fetch when undici cannot load.
// Undici is unavailable in this runtime; callers fall back themselves.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c19158a812

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

},
"engines": {
"node": ">=20.18.1 <21 || >=22"
"node": ">=22.19.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Align the CLI's Node floor with the SDK

When the changesets release process updates @e2b/cli's workspace:^ dependency to this new SDK major, the CLI will still advertise Node >=20.18.1 <21 || >=22.9.0, while this dependency rejects every version below 22.19.0. Consequently, installing the current CLI on its documented Node 20 or Node 22.9–22.18 range fails for users with strict engine enforcement (including this repository's pnpm configuration), before the intended global-fetch fallback can run. Update the CLI engine range and changeset/tests as part of this breaking release, or retain a compatible SDK engine range for the fallback path.

Useful? React with 👍 / 👎.

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Ran offline end-to-end tests of the undici 8-only dependency change (no live E2B key; local servers driven through the SDK's real src/undici.ts code paths on Node 24).

Key result — undici 8 vs the h2 stream-limit failure mode:

local node:http2 TLS server, SETTINGS_MAX_CONCURRENT_STREAMS=2
undici 8.10.0 Agent({ allowH2: true, connections: 3 }), 9 concurrent fetches:

fulfilled OK: 9/9
errors: none
server sessions (connections) opened: 3      <- spilled to extra connections
peak concurrent streams per session: 2       <- respected the advertised limit
PASS  no ERR_HTTP2_TOO_MANY_CONCURRENT_STREAMS

Version selection & dispatch path:

PASS  loadUndici() resolves module; resolved undici version = 8.10.0
PASS  getUndiciPackageCandidates: 22.19.0/24.19.0 -> ['undici']; 22.18.0/18.20.0 -> []
PASS  buildDispatchedFetch request provably served via undici.fetch (counting wrapper)
Global-fetch fallback (old Node simulated) + unit tests
buildDispatchedFetch({ loadUndici: async () => undefined, inflightLimit: 1 }):
PASS  fallback serves plain request (200 + JSON)
PASS  2nd request NOT dispatched while the single slot is held; completes after (no deadlock)
PASS  undici.fetch never invoked on the fallback path

Unit tests: tests/undici.test.ts 9/9 on Node 24 (MockAgent tests running against undici 8).

Not covered: live E2B API traffic (offline-only test environment).

Tested by Devin — session link

@mishushakov

Copy link
Copy Markdown
Member

@devin-ai-integration we should also remove all the inflight machinery

@devin-ai-integration devin-ai-integration Bot changed the title feat(js-sdk)!: drop undici 7, depend on undici@^8 only feat(js-sdk)!: drop undici 7 and the in-flight cap, depend on undici@^8 only Aug 20, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Done in 245f93b: removed src/api/inflight.ts (limitConcurrency), the inflightLimit plumbing in buildDispatchedFetch/createApiFetchForRuntime/createEnvdFetchForRuntime, the E2B_API_INFLIGHT_REQUESTS / E2B_ENVD_INFLIGHT_REQUESTS / E2B_ENVD_RPC_INFLIGHT_REQUESTS env vars, and their tests. Concurrency is now governed entirely by undici's dispatcher (server-advertised h2 stream limits + the E2B_*_CONNECTIONS pools).

Note this supersedes #1717 — with no cap there's no slot accounting to fix, so #1717 can be closed once this lands. One trade-off to be aware of: the global-fetch fallback paths (Node < 22.19, Bun/Deno/workerd/browsers) now have no client-side concurrency bound at all.

devin-ai-integration[bot]

This comment was marked as resolved.

…^8 only

undici 8 honors the server's SETTINGS_MAX_CONCURRENT_STREAMS per h2
connection and spills to additional connections when saturated, so the
dual undici/undici8 selection is no longer needed. Requires Node.js
>= 22.19.0 (undici 8's engine floor); older Node falls back to the
global fetch instead of loading undici.

Concurrency is governed by undici 8's dispatcher: per-connection HTTP/2
stream limits advertised by the server plus the configured connection
pools. Removes limitConcurrency and the E2B_API_INFLIGHT_REQUESTS,
E2B_ENVD_INFLIGHT_REQUESTS, and E2B_ENVD_RPC_INFLIGHT_REQUESTS env vars.

Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1787234613-drop-undici7 branch from 245f93b to d722a06 Compare October 13, 2026 14:25
devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread packages/js-sdk/src/undici.ts Outdated
@mishushakov
mishushakov merged commit 6a74c39 into main Oct 13, 2026
44 of 48 checks passed
@mishushakov
mishushakov deleted the devin/1787234613-drop-undici7 branch October 13, 2026 14:43
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