Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add original error property #2327

Merged
merged 4 commits into from Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 6 additions & 5 deletions documentation/8-errors.md
Expand Up @@ -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`

Expand Down Expand Up @@ -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`

Expand All @@ -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`**

Expand Down
2 changes: 1 addition & 1 deletion source/core/errors.ts
Expand Up @@ -27,7 +27,7 @@ export class RequestError<T = unknown> extends Error {
readonly timings?: Timings;

constructor(message: string, error: Partial<Error & {code?: string}>, self: Request | Options) {
super(message);
super(message, {cause: error});
Error.captureStackTrace(this, this.constructor);

this.name = 'RequestError';
Expand Down
23 changes: 23 additions & 0 deletions test/error.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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<RequestError>(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<RequestError>(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;
Expand Down