Skip to content

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

Open
devin-ai-integration[bot] wants to merge 3 commits into
mainfrom
devin/1787234613-drop-undici7
Open

feat(js-sdk)!: drop undici 7 and the in-flight cap, depend on undici@^8 only#1733
devin-ai-integration[bot] wants to merge 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)): drop the dual undici/undici8 selection introduced in #1575, depend on undici@^8 only, and remove the SDK-level in-flight request cap entirely (per #1733 (comment)).

Why: undici 8 gates h2 dispatch on the server's SETTINGS_MAX_CONCURRENT_STREAMS (its busy() returns true when running >= maxConcurrentStreams) and the pool spills to additional connections when one saturates, then queues — it never throws ERR_HTTP2_TOO_MANY_CONCURRENT_STREAMS. That makes both undici 7 and the SDK's own semaphore redundant. With Node 20 EOL (April 2026), the only reason to keep undici 7 was Node 22.0–22.18, which just needs a patch update.

 export function getUndiciPackageCandidates(nodeVersion: string): string[] {
-  if (compareVersions(nodeVersion, '22.19.0') >= 0) return ['undici8', 'undici']
-  return ['undici']
+  if (compareVersions(nodeVersion, '22.19.0') >= 0) return ['undici']
+  return []
 }
  • package.json: undici: ^8.10.0 as a regular dependency, undici8 alias removed, engines.node narrowed from >=20.18.1 <21 || >=22 to >=22.19.0 (undici 8's engine floor) — hence the major changeset.
  • In-flight machinery removed: src/api/inflight.ts (limitConcurrency) is deleted, buildDispatchedFetch no longer takes inflightLimit, and the E2B_API_INFLIGHT_REQUESTS / E2B_ENVD_INFLIGHT_REQUESTS / E2B_ENVD_RPC_INFLIGHT_REQUESTS env vars are gone. Concurrency is governed by undici's dispatcher (server-advertised per-connection h2 stream limits + the E2B_API_CONNECTIONS/E2B_ENVD_RPC_CONNECTIONS pools).
  • On Node older than 22.19.0 the SDK loads no undici and uses the existing global-fetch fallback in buildDispatchedFetch, so e.g. the CLI on old Node degrades gracefully instead of crashing.
  • .tool-versions + codegen.Dockerfile: nodejs 22.18.0 → 22.19.0 so CI exercises the undici path (22.18 would silently take the global-fetch fallback).

Usage is unchanged; no user-facing API changes beyond the Node requirement and the removed env vars:

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

This supersedes #1717: with the cap gone there is no slot accounting to fix.

Verified: pnpm run format / lint / typecheck, js-sdk build, and the undici/api/envd http2 unit tests (including the real-undici MockAgent tests now running against undici 8) all pass locally on Node 24. Offline e2e evidence in the comment below.

Link to Devin session: https://app.devin.ai/sessions/66bc0b6c5e204bb98db02aacffea68fe
Requested by: @mishushakov

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
capped global fetch instead of loading undici.

Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@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: 245f93b

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

This PR includes changesets to release 2 packages
Name Type
e2b Major
@e2b/cli Patch

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 1a47afc. Download artifacts from this workflow run.

JS SDK (e2b@2.42.1-devin-1787234613-drop-undici7.0):

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

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

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

Python SDK (e2b==2.42.0+devin.1787234613.drop.undici7):

pip install ./e2b-2.42.0+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 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

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 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.

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