diff --git a/documentation/8-errors.md b/documentation/8-errors.md index 2cb97aa7d..ede9f8d6a 100644 --- a/documentation/8-errors.md +++ b/documentation/8-errors.md @@ -19,8 +19,9 @@ All Got errors contain various metadata, such as: Read the article [here](async-stack-traces.md). -**Note:** +> [!NOTE] > - The error codes may differ when the root error has a `code` property set. +> - The root error will be propagated as is via the [`cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) property. ### `RequestError` @@ -62,8 +63,8 @@ A request is successful when the status code of the final request is `2xx` or `3 When [following redirects](2-options.md#followredirect), a request is successful **only** when the status code of the final request is `2xx`. -**Note:** -> - `304` responses are always considered successful. +> [!NOTE] +> `304` responses are always considered successful. ### `MaxRedirectsError` @@ -73,8 +74,8 @@ When the server redirects you more than ten times. Includes a `response` propert ### `UnsupportedProtocolError` -**Note:** -> - This error is not public. +> [!NOTE] +> This error is not public. **Code: `ERR_UNSUPPORTED_PROTOCOL`** diff --git a/source/core/errors.ts b/source/core/errors.ts index 07e50705f..cda0f7cc2 100644 --- a/source/core/errors.ts +++ b/source/core/errors.ts @@ -27,7 +27,7 @@ export class RequestError extends Error { readonly timings?: Timings; constructor(message: string, error: Partial, self: Request | Options) { - super(message); + super(message, {cause: error}); Error.captureStackTrace(this, this.constructor); this.name = 'RequestError'; diff --git a/test/error.ts b/test/error.ts index d915de231..273e08308 100644 --- a/test/error.ts +++ b/test/error.ts @@ -4,6 +4,7 @@ import net from 'node:net'; import http from 'node:http'; import stream from 'node:stream'; import {pipeline as streamPipeline} from 'node:stream/promises'; +import {Agent} from 'node:https'; import test from 'ava'; import getStream from 'get-stream'; import is from '@sindresorhus/is'; @@ -359,6 +360,28 @@ test.skip('the old stacktrace is recovered', async t => { t.not(error?.stack!.indexOf('at get'), error?.stack!.lastIndexOf('at get')); }); +test('should wrap got cause', async t => { + const error = await t.throwsAsync(got('https://github.com', {retry: {limit: 0}, timeout: {request: 1}})); + const cause = error?.cause as TimeoutError; + t.is(error?.code, cause.code); + t.is(error?.message, cause.message); +}); + +test('should wrap non-got cause', async t => { + class SocksProxyAgent extends Agent { + createConnection() { + throw new SocksClientError('oh no'); + } + } + class SocksClientError extends Error {} + const error = await t.throwsAsync(got('https://github.com', {retry: {limit: 0}, timeout: {read: 1}, agent: {https: new SocksProxyAgent()}})); + const cause = error?.cause as Error; + t.is(error?.code, 'ERR_GOT_REQUEST_ERROR'); + t.is(error?.message, cause.message); + t.is(error?.message, 'oh no'); + t.assert(cause instanceof SocksClientError); +}); + test.serial('custom stack trace', withServer, async (t, _server, got) => { // eslint-disable-next-line @typescript-eslint/naming-convention const ErrorCaptureStackTrace = Error.captureStackTrace;