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
TypeError
is thrown, thecause
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);
}
});