undici
semver
>=5.0.0postconditions25functions16last verified2026-06-18coverage score100%Postconditions — what we check
- request · network-error-handlingerrorWhennetwork failure, DNS error, timeout, connection refusedThrows
Promise rejection with ErrorRequired handlingUse try-catch (async/await) or .catch() (promises)costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - fetch · network-error-handlingerrorWhennetwork failure, DNS error, timeout, connection refused, certificate errorsThrows
TypeError with 'fetch failed' message and cause property containing underlying error (UND_ERR_SOCKET, etc.)Required handlingUse try-catch (async/await) or .catch() (promises)costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - stream · stream-network-errorerrorWhennetwork failure, DNS error, timeout (UND_ERR_CONNECT_TIMEOUT, UND_ERR_HEADERS_TIMEOUT, UND_ERR_BODY_TIMEOUT, UND_ERR_SOCKET) before or during streamingThrows
ConnectTimeoutError (UND_ERR_CONNECT_TIMEOUT), HeadersTimeoutError (UND_ERR_HEADERS_TIMEOUT), BodyTimeoutError (UND_ERR_BODY_TIMEOUT), SocketError (UND_ERR_SOCKET)Required handlingWrap await stream() call in try-catch; check error.code for specific timeout vs socket failurecostmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - stream · stream-factory-invalid-returnerrorWhenfactory function returns non-Writable valueThrows
InvalidReturnValueError (UND_ERR_INVALID_RETURN_VALUE)Required handlingWrap await stream() in try-catch; factory must return a stream.Writablecostlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - connect · connect-network-errorerrorWhenconnection refused, timeout, or proxy authentication failureThrows
ConnectTimeoutError (UND_ERR_CONNECT_TIMEOUT), SocketError (UND_ERR_SOCKET), SecureProxyConnectionError (UND_ERR_PRX_TLS)Required handlingWrap await connect() in try-catch; check error.code for UND_ERR_CONNECT_TIMEOUT vs UND_ERR_PRX_TLScostmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - upgrade · upgrade-network-errorerrorWhennetwork failure or server rejects upgrade requestThrows
ConnectTimeoutError (UND_ERR_CONNECT_TIMEOUT), SocketError (UND_ERR_SOCKET), HeadersTimeoutError (UND_ERR_HEADERS_TIMEOUT)Required handlingWrap await upgrade() in try-catchcostmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - json · response-json-parse-errorerrorWhenserver returns non-JSON body (HTML error page, empty body, binary content)Throws
SyntaxError: Unexpected token in JSONRequired handlingWrap await response.json() in try-catch; always check response.ok before calling .json()costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - json · response-ok-not-checkedwarningWhenresponse.json() called without checking response.ok firstThrows
SyntaxError when server returns error page instead of JSONRequired handlingAlways check response.ok or response.status before calling response.json()costmediumin prodsilent failureusers seelost datavisibilitysilent - text · response-text-no-try-catcherrorWhenresponse.text() called in async context without try-catch wrapping the await. Network failure during body streaming (UND_ERR_SOCKET, UND_ERR_BODY_TIMEOUT) after headers arrive causes the body promise to reject.Throws
BodyTimeoutError (UND_ERR_BODY_TIMEOUT) if body download exceeds bodyTimeout. SocketError (UND_ERR_SOCKET) if TCP connection drops mid-body. RequestAbortedError (UND_ERR_ABORTED) if AbortSignal fires during body read.Required handlingWrap await response.text() in try-catch. Network can fail AFTER headers are received — a successful fetch() does not guarantee the body will arrive intact. Critical in streaming scenarios or large response bodies.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - text · response-text-body-already-readerrorWhenresponse.text() called after the body has already been consumed by a previous call to .text(), .json(), .arrayBuffer(), .blob(), .bytes(), or .body.getReader().Throws
TypeError: 'Body is unusable: Body has already been read' (confirmed from undici/lib/web/fetch/body.js source)Required handlingCaller MUST call response.text() only once per Response object. If the same response body needs to be read multiple times, clone the Response before consuming: const cloned = response.clone(); await cloned.text(). Alternatively, store the result in a variable and reuse it.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - arrayBuffer · response-arraybuffer-no-try-catcherrorWhenresponse.arrayBuffer() called without try-catch. Body streaming errors (timeout, socket drop) cause the promise to reject.Throws
BodyTimeoutError (UND_ERR_BODY_TIMEOUT), SocketError (UND_ERR_SOCKET), RequestAbortedError (UND_ERR_ABORTED), ResponseExceededMaxSizeError (UND_ERR_RES_EXCEEDED_MAX_SIZE) when Client configured with maxResponseSize.Required handlingWrap await response.arrayBuffer() in try-catch. Large binary downloads are especially vulnerable to BodyTimeoutError. If a maxResponseSize limit is configured on the Client, ResponseExceededMaxSizeError will be thrown for files that exceed the limit.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - arrayBuffer · response-arraybuffer-body-already-readerrorWhenresponse.arrayBuffer() called after the body has already been consumed by a previous call to any body-reading method or .body.getReader().Throws
TypeError: 'Body is unusable: Body has already been read'Required handlingUse response.clone() before consuming the body if multiple reads are needed. Store the ArrayBuffer result in a variable rather than calling arrayBuffer() twice.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - WebSocket · websocket-constructor-syntax-errorwarningWhennew WebSocket(url) called with an invalid URL format, non-ws/wss scheme, or duplicate/invalid subprotocol strings in the protocols array.Throws
DOMException with name 'SyntaxError'. Thrown synchronously from the constructor, not from an event or promise. Occurs immediately when new WebSocket() is called.Required handlingWrap new WebSocket(url) in try-catch if the URL comes from user input or configuration. Static URLs (hardcoded ws://...) are safe to skip try-catch. Dynamic URLs from config files or env vars should be validated or wrapped.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - WebSocket · websocket-connection-error-not-handlederrorWhenWebSocket connection failure (server unreachable, TLS handshake failure, server rejected upgrade) occurs but no 'onerror' or 'error' event handler is registered. Connection errors are dispatched via the error event, NOT thrown from the constructor.Throws
Does NOT throw. Instead dispatches an ErrorEvent to the 'error' event listener. If no error listener is registered, the error is silently swallowed in browsers but in Node.js the uncaught error causes process crash (UnhandledErrorEvent). After the error event, the 'close' event fires with code 1006 (abnormal closure).Required handlingAlways register ws.onerror or ws.addEventListener('error', handler) before the connection attempt. A missing error handler means connection failures are invisible. Example: const ws = new WebSocket('wss://api.example.com/ws'); ws.onerror = (event) => { logger.error('WebSocket connection failed', event); }; ws.onclose = (event) => { if (!event.wasClean) reconnect(); };costmediumin prodimmediate exceptionusers seeservice unavailablevisibilitysilent - close · dispatcher-close-already-destroyedwarningWhenclient.close() / pool.close() called AFTER client.destroy() has already been called. The close() promise will reject with ClientDestroyedError.Throws
ClientDestroyedError (UND_ERR_DESTROYED): 'The client is destroyed' Confirmed from undici/lib/dispatcher/dispatcher-base.js source.Required handlingTrack dispatcher lifecycle state. In graceful shutdown handlers, do not call close() if destroy() may have been called on error paths. Use the destroyed property to check: if (!client.destroyed) await client.close(). In finally blocks that attempt cleanup: wrap close() in try-catch.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - blob · response-blob-no-try-catcherrorWhenresponse.blob() called without try-catch. Body streaming errors (timeout, socket drop, exceeded max response size) cause the promise to reject.Throws
BodyTimeoutError (UND_ERR_BODY_TIMEOUT) if body download exceeds bodyTimeout. SocketError (UND_ERR_SOCKET) if TCP connection drops mid-body. RequestAbortedError (UND_ERR_ABORTED) if AbortSignal fires during body read. ResponseExceededMaxSizeError (UND_ERR_RES_EXCEEDED_MAX_SIZE) when Client is configured with maxResponseSize limit.Required handlingWrap await response.blob() in try-catch. Like arrayBuffer(), blob() buffers the entire body in memory, so large downloads can hit ResponseExceededMaxSizeError when maxResponseSize is configured on the Client. Binary downloads have a higher rate of bodyTimeout failures than text responses.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - blob · response-blob-body-already-readerrorWhenresponse.blob() called after the body has already been consumed by any prior call to .text(), .json(), .arrayBuffer(), .bytes(), .formData(), .blob(), or .body.getReader().Throws
TypeError: 'Body is unusable: Body has already been read' (confirmed from undici/lib/web/fetch/body.js line 467)Required handlingUse response.clone() before consuming the body if multiple reads are needed. Store the Blob result in a variable rather than calling blob() twice.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - bytes · response-bytes-no-try-catcherrorWhenresponse.bytes() called without try-catch. Body-streaming errors (timeout, socket drop, exceeded max response size) cause the promise to reject.Throws
BodyTimeoutError (UND_ERR_BODY_TIMEOUT), SocketError (UND_ERR_SOCKET), RequestAbortedError (UND_ERR_ABORTED), ResponseExceededMaxSizeError (UND_ERR_RES_EXCEEDED_MAX_SIZE).Required handlingWrap await response.bytes() in try-catch. Behavior matches arrayBuffer() — large binary payloads can hit bodyTimeout (default 300s) or ResponseExceededMaxSizeError when a Client is configured with maxResponseSize. Unhandled rejections crash the Node.js process under default settings.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - bytes · response-bytes-body-already-readerrorWhenresponse.bytes() called after the body has already been consumed by a previous call to any body-reading method or .body.getReader().Throws
TypeError: 'Body is unusable: Body has already been read'Required handlingUse response.clone() before consuming the body if multiple reads are needed. Store the Uint8Array result in a variable rather than calling bytes() twice.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - formData · response-formdata-no-try-catcherrorWhenresponse.formData() called without try-catch. Body-streaming errors AND multipart parsing errors AND unsupported Content-Type all cause rejection.Throws
BodyTimeoutError (UND_ERR_BODY_TIMEOUT), SocketError (UND_ERR_SOCKET), RequestAbortedError (UND_ERR_ABORTED). TypeError 'Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded"' if the server returned a body with a non-form Content-Type (e.g. application/json error responses). TypeError from multipart parser if multipart boundaries are malformed.Required handlingWrap await response.formData() in try-catch. formData() has the highest probability of throwing among body-consumer methods because it can fail for THREE distinct reasons: network errors, multipart-parse errors, and unsupported Content-Type. Always check response.ok first — error responses rarely return form-encoded bodies, so .formData() on an HTTP 500 with a JSON error body will throw the Content-Type TypeError.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - formData · response-formdata-unsupported-content-typewarningWhenresponse.formData() called on a response whose Content-Type is not multipart/form-data or application/x-www-form-urlencoded (e.g. an HTTP 500 response with application/json or text/html body).Throws
TypeError: 'Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".' Confirmed from undici/lib/web/fetch/body.js line 380-384.Required handlingCheck response.headers.get('content-type') before calling .formData(), OR always wrap the await in try-catch and treat the TypeError as a parsing failure (not a network failure). Webhook proxies should validate Content-Type explicitly to distinguish bad-payload from network-error.costlowin prodimmediate exceptionusers seelost datavisibilityvisible - pipeline · pipeline-missing-error-listenererrorWhenundici.pipeline() return value used without registering an 'error' event listener on the returned Duplex. Network failures (UND_ERR_SOCKET, UND_ERR_CONNECT_TIMEOUT, UND_ERR_BODY_TIMEOUT) are dispatched as 'error' events on the Duplex — they do NOT throw synchronously and do NOT reject a Promise.Throws
Does NOT throw synchronously. Error is emitted as 'error' event on the returned Duplex stream. If no 'error' listener is registered, Node.js treats it as an uncaught exception and crashes the process under default configuration.Required handlingAlways register an 'error' listener on the Duplex returned by pipeline(), OR pass the Duplex to stream.pipeline() / pipeline.promisify() which handles the error path. Direct usage: const duplex = undici.pipeline(url, opts, handler); duplex.on('error', (err) => { logger.error(err); }); Or via stream/promises: await pipeline(source, undici.pipeline(url, opts, handler), dest);costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - destroy · dispatcher-destroy-no-try-catcherrorWhendispatcher.destroy() called in async context without try-catch wrapping the await. Pending request callbacks reject with the destroy error, and the destroy() promise itself may reject if invalid arguments are passed.Throws
InvalidArgumentError (UND_ERR_INVALID_ARG) if a non-function callback is passed synchronously. The returned Promise resolves successfully in normal shutdown, but rejection-handling is required when destroy() is called inside finally blocks where errors may already be in flight.Required handlingIn emergency shutdown paths (process SIGTERM, error recovery), wrap await dispatcher.destroy() in try-catch. Destroying a Pool/Agent during active request handling can cause request callbacks to fire with the destroy error — those handlers must also be try-catched. Pattern: try { await client.destroy(new Error('shutdown')); } catch (e) { logger.warn('destroy failed', e); }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - EventSource · eventsource-constructor-syntax-errorwarningWhennew EventSource(url) called with an invalid URL format that fails URL parsing per the WHATWG URL spec.Throws
DOMException with name 'SyntaxError'. Thrown synchronously from the constructor. Confirmed from undici/lib/web/eventsource/eventsource.js line 143-144.Required handlingWrap new EventSource(url) in try-catch if the URL comes from user input, query strings, env vars, or configuration. Hardcoded URLs are safe to skip the try-catch. Dynamic URLs (e.g. SSE endpoint built from request params) must be validated or wrapped.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - EventSource · eventsource-connection-error-not-handlederrorWhenEventSource connection failure (server unreachable, TLS handshake failure, non-2xx response, malformed SSE stream) occurs but no 'onerror' or 'error' event listener is registered. Connection errors are dispatched via the error event, NOT thrown from the constructor.Throws
Does NOT throw. Instead dispatches an Event of type 'error' to the 'error' listener. If no error listener is registered, Node.js treats the EventTarget error event as silent — but EventSource then enters CONNECTING state and retries indefinitely, with no observability of the failure.Required handlingAlways register es.onerror or es.addEventListener('error', handler) before the connection attempt. Without it, SSE connection failures are invisible AND EventSource silently retries forever — a hung SSE consumer that never receives data with no log signal. Example: const es = new EventSource('https://api.example.com/events'); es.onerror = (event) => { logger.error('SSE connection failed, readyState=' + es.readyState); if (es.readyState === EventSource.CLOSED) es.close(); };costmediumin prodsilent failureusers seelost datavisibilitysilent
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
- [1]npmjs.com/package/undicihttps://www.npmjs.com/package/undici
- [2]undici.nodejs.orghttps://undici.nodejs.org/
- [3]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/docs/docs/api/Errors.md
- [4]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/json
- [5]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/ok
- [6]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/text#exceptions
- [7]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/text
- [8]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/lib/web/fetch/body.js
- [9]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/lib/dispatcher/client-h1.js
- [10]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/arrayBuffer
- [11]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#exceptions
- [12]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/lib/web/websocket/websocket.js
- [13]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/WebSocket#events
- [14]rfc-editor.org/rfc/rfc6455https://www.rfc-editor.org/rfc/rfc6455
- [15]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/lib/dispatcher/dispatcher-base.js
- [16]undici.nodejs.orghttps://undici.nodejs.org/#/docs/api/Dispatcher
- [17]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/blob
- [18]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/bytes
- [19]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/Response/formData
- [20]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/lib/api/api-pipeline.js
- [21]undici.nodejs.orghttps://undici.nodejs.org/#/docs/api/Dispatcher.md?id=dispatcherpipelineoptions-handler
- [22]undici.nodejs.orghttps://undici.nodejs.org/#/docs/api/Dispatcher.md?id=dispatcherdestroyerror-callback
- [23]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
- [24]github.com/nodejs/undicihttps://github.com/nodejs/undici/blob/main/lib/web/eventsource/eventsource.js
- [25]developer.mozilla.org/en-US/docshttps://developer.mozilla.org/en-US/docs/Web/API/EventSource#events
- [26]html.spec.whatwg.org/multipage/server-sent-events.htmlhttps://html.spec.whatwg.org/multipage/server-sent-events.html
Need a different package?
Request a profile