Skip to content

Unhandled SocketError: other side closed from undici HTTP/2 client crashes long-running data commands #3622

Description

@torynet

Summary

Since @jsforce/jsforce-node moved to undici 8.x, all Salesforce API traffic negotiates HTTP/2 by default (undici 8 changed the default of allowH2 from false to true). When Salesforce closes the HTTP/2 connection during a long-running command (idle timeout / GOAWAY mid-import), the resulting socket error is emitted as an unhandled 'error' event and the entire Node process crashes. Long sf data import tree runs cannot complete.

Steps To Reproduce

  1. sf data import tree --plan <plan.json> with a large plan (~51,000 records across 7 sObjects, roughly a 20-minute import) against a sandbox.
  2. Partway through the import (observed anywhere from seconds to ~12 minutes in), the process crashes.

Because the crash is an unhandled exception, --json produces no output at all — the command dies silently from a script's perspective, with only the Node crash dump on stderr.

Actual result

node:events:486
      throw er; // Unhandled 'error' event
      ^

SocketError: other side closed
    at TLSSocket.onHttp2SocketEnd (...\@salesforce\cli\node_modules\@jsforce\jsforce-node\node_modules\undici\lib\dispatcher\client-h2.js:602:22)
    at TLSSocket.emit (node:events:520:35)
    at endReadableNT (node:internal/streams/readable:1701:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:90:21)
Emitted 'error' event on ClientHttp2Stream instance at:
    at emitErrorNT (node:internal/streams/destroy:170:8)
    at emitErrorCloseNT (node:internal/streams/destroy:129:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
  code: 'UND_ERR_SOCKET',
  ...
}

Expected result

The import completes, or at minimum the connection-closed error is caught and surfaced as a normal command error (ideally retried, since it is a transient transport condition).

Analysis

  • undici 8 enables HTTP/2 by default: lib/core/connect.jsallowH2 = allowH2 != null ? allowH2 : true. undici 7 defaulted to false.
  • @jsforce/jsforce-node (3.10.18/3.10.19, dependency undici: ^8.5.0) never passes allowH2 and does not handle the 'error' event undici's h2 client emits when the server closes the connection, so the error escapes to the process level.
  • Confirmed by workaround: patching the bundled undici to allowH2 = false (forcing HTTP/1.1) makes the identical 51k-record import complete cleanly, first try.
  • Note: pinning an older CLI version does not avoid this, because npm re-resolves jsforce-node's undici ^8.5.0 range at install time.

Suggested fix

Either pass allowH2: false in jsforce-node's undici dispatcher options until h2 error handling is robust, or attach an error handler for UND_ERR_SOCKET on h2 streams and retry/surface it as a request error. (Likely belongs in jsforce/jsforce — filing here first since sf data import tree is the user-facing failure.)

System Information

  • Shell: PowerShell 7 / Git Bash
  • OS: Windows 11 Home 10.0.26200
  • sf --version: @salesforce/cli/2.146.3 win32-x64 node-v24.10.0 (also reproduced on a fresh npm install of 2.142.6, which resolves the same jsforce-node/undici versions)
  • @jsforce/jsforce-node: 3.10.19 (2.146.3) / 3.10.18 (2.142.6), both with undici 8.5.0
  • Node: 24.10.0, npm 11.6.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssue or pull request that identifies or fixes a buginvestigatingWe're actively investigating this issuevalidatedVersion information for this issue has been validated

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions