Profiles·Public

node-fetch

semver>=2.0.0postconditions16functions8last verified2026-06-23coverage score100%

Postconditions: what we check

  • fetch · fetch-rejects-on-network-error
    error
    Whennetwork failure (DNS resolution failure, connection refused, timeout, etc.)
    ThrowsPromise rejection with FetchError (error.type='system', error.code like ENOTFOUND, ECONNREFUSED, ETIMEDOUT)
    Required handlingCaller MUST use try-catch to handle Promise rejections from fetch(). Network-level failures (DNS errors, connection refused, timeouts) reject the Promise. CRITICAL: fetch() does NOT reject on HTTP 4xx/5xx status codes - those are successful responses that must be checked with response.ok. Recommended pattern: try { const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); } catch (error) { /* handle network errors */ }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • fetch · fetch-rejects-on-abort
    error
    Whenrequest is cancelled via AbortSignal before or during the request
    ThrowsPromise rejection with AbortError (error.name='AbortError', error.type='aborted')
    Required handlingWhen using AbortController to cancel requests, the caller MUST handle AbortError separately from FetchError. AbortError is thrown when signal.abort() is called. Pattern: try { const res = await fetch(url, { signal }); } catch (err) { if (err.name === 'AbortError') { /* handle cancellation */ } else throw err; }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1][2]
  • fetch · fetch-rejects-on-redirect-error
    warning
    Whenredirect:error option set and server responds with redirect, OR redirect limit (default 20) exceeded
    ThrowsPromise rejection with FetchError (error.type='no-redirect' or error.type='max-redirect')
    Required handlingWhen fetch() is called with redirect:'error', any redirect response causes rejection with FetchError (error.type='no-redirect'). When redirect limit (default 20) is exhausted, rejects with FetchError (error.type='max-redirect'). Both must be caught.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • fetch · fetch-http-error-not-thrown
    error
    Whenserver responds with HTTP 4xx or 5xx status code
    ThrowsDOES NOT throw — resolves with Response object where response.ok === false
    Required handlingCRITICAL FOOTGUN: Unlike axios, node-fetch does NOT reject on HTTP error status codes. HTTP 4xx/5xx resolve successfully to a Response with response.ok === false. Callers MUST explicitly check response.ok or response.status before processing body. Failure silently processes error responses (HTML error pages, error JSON) as valid data. Pattern: if (!response.ok) throw new Error(`HTTP error ${response.status}`);
    costhighin prodsilent failureusers seelost datavisibilitysilent
    Sources[2]
  • response.json · response-json-throws-on-invalid-json
    error
    Whenresponse body is not valid JSON (HTML error pages, plain text, empty body from 204 responses)
    ThrowsSyntaxError from JSON.parse() — 'Unexpected token < in JSON at position 0' or similar
    Required handlingresponse.json() calls JSON.parse() internally. If the server returns HTML (e.g., error page, WAF block, load balancer error), SyntaxError is thrown. Caller MUST wrap in try-catch. Combine with response.ok check to handle HTTP errors before attempting to parse body. Pattern: if (!response.ok) throw new Error(...); const data = await response.json();
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • response.json · response-json-throws-on-max-size
    warning
    Whenresponse body size exceeds the size limit set in RequestInit.size option
    ThrowsFetchError (error.type='max-size') — 'content size at <url> over limit: <size>'
    Required handlingIf fetch() was called with a size limit option and the response body exceeds it, response.json() throws FetchError with error.type='max-size'. Must be caught with try-catch around response.json().
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • response.json · response-json-throws-on-body-used
    warning
    Whenresponse.json() called after body stream has already been consumed by any body-reading method
    ThrowsTypeError — 'body used already for: <url>'
    Required handlingResponse bodies can only be consumed once. Calling response.json() after any other body-consuming method (json, text, blob, arrayBuffer, formData) throws TypeError. Use response.clone() to read the body multiple times. Pattern: const cloned = response.clone(); const data = await cloned.json();
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • response.text · response-text-throws-on-stream-error
    error
    Whenresponse body stream encounters a system-level error during reading (premature close, network drop after headers received)
    ThrowsFetchError (error.type='system') — 'Invalid response body while trying to fetch <url>'
    Required handlingEven after a successful fetch() (headers received, Promise resolved), the body stream can fail during reading. response.text() MUST be wrapped in try-catch. FetchError with type='system' indicates a stream-level I/O error. Common mistake: wrapping only fetch() in try-catch, leaving response.text() unguarded.
    costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[1]
  • response.text · response-text-throws-on-premature-close
    error
    Whenserver closes the connection before the full body is received
    ThrowsFetchError — 'Premature close of server response while trying to fetch <url>'
    Required handlingIf the server closes the TCP connection before sending the full response body, response.text() throws FetchError. This occurs after headers are received but before body completes — distinct from initial fetch() network failure. Must be caught with try-catch around response.text().
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • response.blob · response-blob-throws-on-stream-error
    error
    Whenresponse body stream fails during reading (system error, premature close, max-size exceeded)
    ThrowsFetchError (error.type='system' or 'max-size') — same errors as response.text()
    Required handlingresponse.blob() delegates to arrayBuffer() which uses the same consumeBody() stream path. Must be wrapped in try-catch to handle FetchError on stream errors. Same error types apply as response.text() and response.arrayBuffer().
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • response.arrayBuffer · response-arraybuffer-throws-on-stream-error
    error
    Whenresponse body stream fails during reading (system error, premature close, or size limit exceeded)
    ThrowsFetchError (error.type='system') or FetchError (error.type='max-size') if size limit exceeded
    Required handlingresponse.arrayBuffer() uses the same consumeBody() stream-reading path. IMPORTANT for file downloads: max-size option on the original fetch() call will cause FetchError here if the file is larger than the limit. Must be wrapped in try-catch.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • response.formData · response-formdata-throws-on-missing-content-type
    error
    Whenresponse has no Content-Type header (server omitted it)
    ThrowsTypeError — 'Cannot read properties of null (reading 'startsWith')' from ct.startsWith()
    Required handlingresponse.formData() reads response.headers.get('content-type') and calls .startsWith() on the result. If the server response omits Content-Type, this returns null and dereferencing throws TypeError. Wrap in try-catch or guard with `if (response.headers.get('content-type'))` before calling formData().
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • response.formData · response-formdata-throws-on-stream-error
    error
    Whenresponse body stream fails during reading (system error, premature close, size limit exceeded) — same FetchError surface as response.text()
    ThrowsFetchError (error.type='system' or 'max-size') — body-consumption failures from consumeBody()
    Required handlingresponse.formData() reads the body via this.text() (for x-www-form-urlencoded) or the multipart parser (for multipart/form-data). Both paths funnel through consumeBody() which throws FetchError on stream-level I/O errors, premature close, or size-limit overrun. Must be wrapped in try-catch around the await.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • response.formData · response-formdata-throws-on-body-used
    warning
    Whenresponse.formData() called after body stream has already been consumed by any body-reading method
    ThrowsTypeError — 'body used already for: <url>'
    Required handlingResponse bodies can only be consumed once. Calling response.formData() after json(), text(), blob(), arrayBuffer(), or another formData() call throws TypeError. Use response.clone() to read the body multiple times.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • blobFrom · blobfrom-rejects-on-fs-stat-failure
    error
    Whenpath does not exist, is inaccessible (permission denied), or is otherwise unreadable by fs.promises.stat()
    ThrowsPromise rejection with NodeJS.ErrnoException (error.code='ENOENT', 'EACCES', 'EISDIR', etc.)
    Required handlingblobFrom(path) calls fs.promises.stat(path) under the hood. Any filesystem failure (missing file ENOENT, permission denied EACCES, directory EISDIR) rejects the Promise with the underlying Node error. Caller MUST wrap in try-catch — common failure modes when uploading user-supplied paths or paths from configuration files that may have moved.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[4]
  • fileFrom · filefrom-rejects-on-fs-stat-failure
    error
    Whenpath does not exist, is inaccessible (permission denied), or is otherwise unreadable by fs.promises.stat()
    ThrowsPromise rejection with NodeJS.ErrnoException (error.code='ENOENT', 'EACCES', 'EISDIR', etc.)
    Required handlingfileFrom(path) calls fs.promises.stat(path) under the hood. Any filesystem failure (missing file ENOENT, permission denied EACCES, directory EISDIR) rejects the Promise with the underlying Node error. Same handling as blobFrom — wrap in try-catch when constructing upload bodies from user-supplied paths.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[4]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Source code

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

Sources: node-fetch

Package: node-fetch Contract Version: 1.0.0 Last Updated: 2026-02-26


Official Documentation

Error Handling Guide

  • URL: https://github.com/node-fetch/node-fetch/blob/main/docs/ERROR-HANDLING.md
  • Relevance: Official documentation on error handling, FetchError types, and network failures
  • Key Points:
    • All operational errors (except aborted requests) are rejected with FetchError
    • Network errors have error.type = 'system' with error.code (ENOTFOUND, ECONNREFUSED, ETIMEDOUT)
    • AbortError for cancelled requests
    • Critical: HTTP 3xx-5xx responses are NOT exceptions and should be handled in then()

NPM Package Page

  • URL: https://www.npmjs.com/package/node-fetch
  • Relevance: Official package documentation and usage examples
  • Key Points:
    • Lightweight fetch() implementation for Node.js
    • Promise-based API matching browser fetch()
    • Supports HTTP/HTTPS requests

Common Error Patterns

Network-Level Errors (Reject Promise)

  1. DNS Resolution Failure - ENOTFOUND
  2. Connection Refused - ECONNREFUSED
  3. Timeout - ETIMEDOUT
  4. Network Unreachable - ENETUNREACH
  5. Aborted Requests - AbortError

HTTP-Level Errors (Do NOT Reject)

  • HTTP 4xx (Client errors) - Response resolves successfully
  • HTTP 5xx (Server errors) - Response resolves successfully
  • Must check response.ok or response.status manually

Nark profile Rationale

Why fetch() Requires Error Handling

Network failures are common and unpredictable:

  • DNS servers may be unavailable
  • Target server may be down
  • Network connectivity issues
  • Firewall blocking connections
  • Request timeouts

Failure to handle rejections leads to:

  • Unhandled promise rejections
  • Application crashes (in newer Node.js versions)
  • Silent failures
  • Poor user experience

Critical Gotcha: HTTP Status Codes

Unlike XMLHttpRequest or many HTTP libraries, fetch() does not reject on HTTP error status codes:

// This will NOT reject even if server returns 404 or 500
const response = await fetch('https://api.example.com/nonexistent');
// response.ok will be false, but Promise resolved successfully

Correct pattern:

try {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  const data = await response.json();
} catch (error) {
  // Handles both network errors AND HTTP errors (if you throw them)
  console.error('Fetch failed:', error);
}

References

  1. node-fetch Error Handling Documentation
  2. Fetch API Error Handling - web.dev
  3. FetchError function - Snyk Advisor
  4. Handling Failed HTTP Responses With fetch()

Version Notes

  • v2.x: Introduced Promise-based API
  • v3.x: ESM-only, dropped CommonJS support
  • Contract applies to v2.0.0+ (covers both v2 and v3 error behavior)
Need a different package?
Request a profile