Proposal: Enhanced Network Error Handling Using Error.cause

Objective

To introduce new error types for specific network-related issues and use the cause property of TypeError to provide detailed information about these errors. This ensures backwards compatibility and leverages existing APIs to enable better error handling.

Motivation

Currently, network-related errors in JavaScript are thrown as TypeError, making it difficult to differentiate between various causes of failure. This proposal aims to enhance error handling by introducing specific error types and using the cause property to provide more granular information.
Why is this an issue? The fetch specification only has a single NetworkError type, but some network errors are recoverable and retries make sense, while others aren't.

A specific example:
A website has two active monitoring tools, both send beacons using fetch keepalive upon user interaction. The fetchGroup is populated with two requests, but the inflightKeepaliveBytes exceed the limit of 64KiB if both request bodies add up to that, causing the second request to fail. This only throws a TypeError (with different messages, depending on the user agent) and the implementation cannot decide if a retry makes sense or not.

If this TypeError would have a cause that allows the implementation to derive if the failure is recoverable, a retry could be issued that would most likely succeed this time.

Proposed Changes

  1. New Error Types:
  • APIError: Thrown when invalid input is provided to the Fetch API.
  • NetworkError: Thrown for general network failures at the TCP/IP layer.
  • AbortError: Thrown when a request is aborted by the user (e.g. via abort signal).
  • OfflineError: Thrown when the browser detects offline status.
  • ContentLimitExceededError: Thrown when the fetch keepalive limit of 64KiB is exceeded.
  1. Using Error.cause:
  • When a network-related TypeError is thrown, the cause property will be set to one of the new error types, providing detailed information about the specific error.

Example Usage

fetch('https://example.com', { keepalive: true })
    .catch(error => {
        if (error instanceof TypeError) {
            const cause = error.cause;
            if (cause instanceof APIError) {
                // not recoverable, implementation issue
            } else if (cause instanceof NetworkError) {
                // maybe recoverable
            } else if (cause instanceof AbortError) {
                // recoverable depending on implementation
            } else if (cause instanceof OfflineError) {
                // recoverable when the navigator is onLine again
            } else if (cause instanceof ContentLimitExceededError) {
                // recoverable by waiting for a "free spot" or with a simple retry
            } else {
                console.error('An unknown error occurred:', error.message);
            }
        } else {
            console.error('An error occurred:', error.message);
        }
    });
2 Likes

The JS language has no concept of "the network" - that's provided by hosts, like web browsers or node. As such, this suggestion belongs in their purview and not in the language itself.

i understand that this definitely requires changes in the fetch spec, but the hosts still need a way to provide the user a possibility to distinguish the kind of errors.

what do you think about hosts instead providing structured data as the error cause, like in the mdn example:

function makeRSA(p, q) {
  if (!Number.isInteger(p) || !Number.isInteger(q)) {
    throw new Error("RSA key generation requires integer inputs.", {
      cause: { code: "NonInteger", values: [p, q] },
    });
  }
  if (!areCoprime(p, q)) {
    throw new Error("RSA key generation requires two co-prime integers.", {
      cause: { code: "NonCoprime", values: [p, q] },
    });
  }
  // rsa algorithm…
}

and in this case of fetch, something like

new TypeError("Failed to fetch", {
  cause:
    {
      code: "ContentLimitExceededError",
      values: [
        numberOfTotalInflightBytes // in case this isn't a security concern
      ]
    }
  }
);

I'm not really in a place to evaluate that. You should ask in the appropriate venue for hosts.