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
- 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.
- Using
Error.cause:
- When a network-related
TypeErroris thrown, thecauseproperty 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);
}
});