diff --git a/readme.md b/readme.md index 82a89c170..e0655aaa7 100644 --- a/readme.md +++ b/readme.md @@ -1705,43 +1705,44 @@ Additionaly, the errors may have `request` (Got Stream) and `response` (Got Resp #### got.RequestError -When a request fails. Contains a `code` property with error class code, like `ECONNREFUSED`. All the errors below inherit this one. +When a request fails. Contains a `code` property with error class code, like `ECONNREFUSED`. If there is no specific code supplied `code` defaults to `ERR_GEN_REQ_ERROR` All the errors below inherit this one. #### got.CacheError -When a cache method fails, for example, if the database goes down or there's a filesystem error. +When a cache method fails, for example, if the database goes down or there's a filesystem error. Contains a `code` property with `ERR_CACHE_ACCESS` or a more specific failure code. #### got.ReadError -When reading from response stream fails. +When reading from response stream fails. Contains a `code` property with `ERR_READING_RESPONSE_STREAM` or a more specific failure code. #### got.ParseError -When server response code is 2xx, and parsing body fails. Includes a `response` property. +When server response code is 2xx, and parsing body fails. Includes a `response` property. Contains a `code` property with `ERR_BODY_PARSE_FAILURE` or a more specific failure code. #### got.UploadError -When the request body is a stream and an error occurs while reading from that stream. +When the request body is a stream and an error occurs while reading from that stream. Contains a `code` property with `ERR_UPLOAD` or a more specific failure code. #### got.HTTPError -When the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304. Includes a `response` property. +When the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304. Includes a `response` property. Contains a `code` property with `ERR_NON_2XX_3XX_RESPONSE` or a more specific failure code. + #### got.MaxRedirectsError -When the server redirects you more than ten times. Includes a `response` property. +When the server redirects you more than ten times. Includes a `response` property. Contains a `code` property with `ERR_MAX_REDIRECTS`. #### got.UnsupportedProtocolError -When given an unsupported protocol. +When given an unsupported protocol. Contains a `code` property with `ERR_UNSUPPORTED_PROTOCOL`. #### got.TimeoutError -When the request is aborted due to a [timeout](#timeout). Includes an `event` and `timings` property. +When the request is aborted due to a [timeout](#timeout). Includes an `event` and `timings` property. Contains a `code` property with `ETIMEDOUT`. #### got.CancelError -When the request is aborted with `.cancel()`. +When the request is aborted with `.cancel()`. Contains a `code` property with `ERR_CANCELED`. ## Aborting the request diff --git a/source/as-promise/types.ts b/source/as-promise/types.ts index ab4d28623..a84962517 100644 --- a/source/as-promise/types.ts +++ b/source/as-promise/types.ts @@ -267,6 +267,7 @@ export class ParseError extends RequestError { super(`${error.message} in "${options.url.toString()}"`, error, response.request); this.name = 'ParseError'; + this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_BODY_PARSE_FAILURE' : this.code; } } @@ -279,6 +280,7 @@ export class CancelError extends RequestError { constructor(request: Request) { super('Promise was canceled', {}, request); this.name = 'CancelError'; + this.code = 'ERR_CANCELED'; } get isCanceled() { diff --git a/source/core/index.ts b/source/core/index.ts index 084d0a3b0..4ca29c4de 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -1191,7 +1191,7 @@ export class RequestError extends Error { Error.captureStackTrace(this, this.constructor); this.name = 'RequestError'; - this.code = error.code; + this.code = error.code ?? 'ERR_GEN_REQ_ERROR'; if (self instanceof Request) { Object.defineProperty(this, 'request', { @@ -1249,6 +1249,7 @@ export class MaxRedirectsError extends RequestError { constructor(request: Request) { super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request); this.name = 'MaxRedirectsError'; + this.code = 'ERR_MAX_REDIRECTS'; } } @@ -1264,6 +1265,7 @@ export class HTTPError extends RequestError { constructor(response: Response) { super(`Response code ${response.statusCode} (${response.statusMessage!})`, {}, response.request); this.name = 'HTTPError'; + this.code = 'ERR_NON_2XX_3XX_RESPONSE'; } } /** @@ -1276,6 +1278,7 @@ export class CacheError extends RequestError { constructor(error: Error, request: Request) { super(error.message, error, request); this.name = 'CacheError'; + this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_CACHE_ACCESS' : this.code; } } @@ -1288,6 +1291,7 @@ export class UploadError extends RequestError { constructor(error: Error, request: Request) { super(error.message, error, request); this.name = 'UploadError'; + this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_UPLOAD' : this.code; } } @@ -1319,6 +1323,7 @@ export class ReadError extends RequestError { constructor(error: Error, request: Request) { super(error.message, error, request); this.name = 'ReadError'; + this.code = this.code === 'ERR_GEN_REQ_ERROR' ? 'ERR_READING_RESPONSE_STREAM' : this.code; } } @@ -1329,6 +1334,7 @@ export class UnsupportedProtocolError extends RequestError { constructor(options: NormalizedOptions) { super(`Unsupported protocol "${options.url.protocol}"`, {}, options); this.name = 'UnsupportedProtocolError'; + this.code = 'ERR_UNSUPPORTED_PROTOCOL'; } } diff --git a/test/cache.ts b/test/cache.ts index e9532211e..89693a814 100644 --- a/test/cache.ts +++ b/test/cache.ts @@ -120,7 +120,10 @@ test('cache error throws `got.CacheError`', withServer, async (t, server, got) = const cache = {}; // @ts-expect-error Error tests - await t.throwsAsync(got({cache}), {instanceOf: got.CacheError}); + await t.throwsAsync(got({cache}), { + instanceOf: got.CacheError, + code: 'ERR_CACHE_ACCESS' + }); }); test('doesn\'t cache response when received HTTP error', withServer, async (t, server, got) => { diff --git a/test/cancel.ts b/test/cancel.ts index 7793d58e9..fa6e16cb4 100644 --- a/test/cancel.ts +++ b/test/cancel.ts @@ -79,7 +79,10 @@ test.serial('does not retry after cancelation', withServerAndFakeTimers, async ( gotPromise.cancel(); }); - await t.throwsAsync(gotPromise, {instanceOf: CancelError}); + await t.throwsAsync(gotPromise, { + instanceOf: CancelError, + code: 'ERR_CANCELED' + }); await t.notThrowsAsync(promise, 'Request finished instead of aborting.'); }); @@ -102,7 +105,10 @@ test.serial('cleans up request timeouts', withServer, async (t, server, got) => } }); - await t.throwsAsync(gotPromise, {instanceOf: CancelError}); + await t.throwsAsync(gotPromise, { + instanceOf: CancelError, + code: 'ERR_CANCELED' + }); // Wait for unhandled errors await delay(40); @@ -124,7 +130,10 @@ test.serial('cancels in-progress request', withServerAndFakeTimers, async (t, se body.push(null); }); - await t.throwsAsync(gotPromise, {instanceOf: CancelError}); + await t.throwsAsync(gotPromise, { + instanceOf: CancelError, + code: 'ERR_CANCELED' + }); await t.notThrowsAsync(promise, 'Request finished instead of aborting.'); }); @@ -144,7 +153,10 @@ test.serial('cancels in-progress request with timeout', withServerAndFakeTimers, body.push(null); }); - await t.throwsAsync(gotPromise, {instanceOf: CancelError}); + await t.throwsAsync(gotPromise, { + instanceOf: CancelError, + code: 'ERR_CANCELED' + }); await t.notThrowsAsync(promise, 'Request finished instead of aborting.'); }); @@ -220,7 +232,10 @@ test.serial('throws on incomplete (canceled) response - promise #2', withServerA promise.cancel(); }); - await t.throwsAsync(promise, {instanceOf: got.CancelError}); + await t.throwsAsync(promise, { + instanceOf: got.CancelError, + code: 'ERR_CANCELED' + }); }); test.serial('throws on incomplete (canceled) response - stream', withServerAndFakeTimers, async (t, server, got, clock) => { @@ -250,7 +265,10 @@ test('throws when canceling cached request', withServer, async (t, server, got) promise.cancel(); }); - await t.throwsAsync(promise, {instanceOf: got.CancelError}); + await t.throwsAsync(promise, { + instanceOf: got.CancelError, + code: 'ERR_CANCELED' + }); }); test('throws when canceling cached request #2', withServer, async (t, server, got) => { @@ -265,5 +283,8 @@ test('throws when canceling cached request #2', withServer, async (t, server, go const promise = got({cache}); promise.cancel(); - await t.throwsAsync(promise, {instanceOf: got.CancelError}); + await t.throwsAsync(promise, { + instanceOf: got.CancelError, + code: 'ERR_CANCELED' + }); }); diff --git a/test/error.ts b/test/error.ts index 9a3161494..b55144cc0 100644 --- a/test/error.ts +++ b/test/error.ts @@ -28,7 +28,7 @@ test('properties', withServer, async (t, server, got) => { // Class fields will always be initialized, even though they are undefined // A test to check for undefined is in place below // t.false({}.hasOwnProperty.call(error, 'code')); - t.is(error.code, undefined); + t.is(error.code, 'ERR_NON_2XX_3XX_RESPONSE'); t.is(error.message, 'Response code 404 (Not Found)'); t.deepEqual(error.options.url, url); t.is(error.response.headers.connection, 'close'); @@ -41,6 +41,7 @@ test('catches dns errors', async t => { t.regex(error.message, /ENOTFOUND|EAI_AGAIN/); t.is(error.options.url.host, 'doesntexist'); t.is(error.options.method, 'GET'); + t.is(error.code, 'ENOTFOUND'); }); test('`options.body` form error message', async t => { @@ -130,6 +131,7 @@ test('`http.request` error', async t => { } }), { instanceOf: got.RequestError, + code: 'ERR_GEN_REQ_ERROR', message: 'The header content contains invalid characters' }); }); @@ -215,7 +217,8 @@ test('promise does not hang on timeout on HTTP error', withServer, async (t, ser await t.throwsAsync(got({ timeout: 100 }), { - instanceOf: TimeoutError + instanceOf: TimeoutError, + code: 'ETIMEDOUT' }); }); diff --git a/test/http.ts b/test/http.ts index a0110701b..da820717f 100644 --- a/test/http.ts +++ b/test/http.ts @@ -92,6 +92,7 @@ test('doesn\'t throw if `options.throwHttpErrors` is false', withServer, async ( test('invalid protocol throws', async t => { await t.throwsAsync(got('c:/nope.com').json(), { instanceOf: UnsupportedProtocolError, + code: 'ERR_UNSUPPORTED_PROTOCOL', message: 'Unsupported protocol "c:"' }); }); @@ -225,6 +226,7 @@ test('throws an error if the server aborted the request', withServer, async (t, }); const error = await t.throwsAsync(got(''), { + code: 'ECONNRESET', message: 'The server aborted pending request' }); diff --git a/test/post.ts b/test/post.ts index 58d2e814f..06196bf31 100644 --- a/test/post.ts +++ b/test/post.ts @@ -354,6 +354,7 @@ test('throws on upload error', withServer, async (t, server, got) => { } })), { instanceOf: UploadError, + code: 'ERR_UPLOAD', message }); }); diff --git a/test/promise.ts b/test/promise.ts index da3e3783e..70dcb937e 100644 --- a/test/promise.ts +++ b/test/promise.ts @@ -71,8 +71,14 @@ test('promise.json() can be called before a file stream body is open', withServe const promise = got({body}); const checks = [ - t.throwsAsync(promise, {instanceOf: CancelError}), - t.throwsAsync(promise.json(), {instanceOf: CancelError}) + t.throwsAsync(promise, { + instanceOf: CancelError, + code: 'ERR_CANCELED' + }), + t.throwsAsync(promise.json(), { + instanceOf: CancelError, + code: 'ERR_CANCELED' + }) ]; promise.cancel(); diff --git a/test/redirects.ts b/test/redirects.ts index c2e555fa7..7d53f4929 100644 --- a/test/redirects.ts +++ b/test/redirects.ts @@ -84,6 +84,7 @@ test('throws on endless redirects - default behavior', withServer, async (t, ser const error = await t.throwsAsync(got(''), {message: 'Redirected 10 times. Aborting.'}); t.deepEqual(error.response.redirectUrls, new Array(10).fill(`${server.url}/`)); + t.is(error.code, 'ERR_MAX_REDIRECTS'); }); test('custom `maxRedirects` option', withServer, async (t, server, got) => { @@ -97,6 +98,7 @@ test('custom `maxRedirects` option', withServer, async (t, server, got) => { const error = await t.throwsAsync(got('', {maxRedirects: 5}), {message: 'Redirected 5 times. Aborting.'}); t.deepEqual(error.response.redirectUrls, new Array(5).fill(`${server.url}/`)); + t.is(error.code, 'ERR_MAX_REDIRECTS'); }); test('searchParams are not breaking redirects', withServer, async (t, server, got) => { @@ -123,7 +125,8 @@ test('redirects GET and HEAD requests', withServer, async (t, server, got) => { }); await t.throwsAsync(got.get(''), { - instanceOf: got.MaxRedirectsError + instanceOf: got.MaxRedirectsError, + code: 'ERR_MAX_REDIRECTS' }); }); @@ -136,7 +139,8 @@ test('redirects POST requests', withServer, async (t, server, got) => { }); await t.throwsAsync(got.post({body: 'wow'}), { - instanceOf: got.MaxRedirectsError + instanceOf: got.MaxRedirectsError, + code: 'ERR_MAX_REDIRECTS' }); }); diff --git a/test/response-parse.ts b/test/response-parse.ts index 963b6a0f2..83bf70715 100644 --- a/test/response-parse.ts +++ b/test/response-parse.ts @@ -90,6 +90,7 @@ test('throws an error on invalid response type', withServer, async (t, server, g t.regex(error.message, /^Unknown body type 'invalid'/); t.true(error.message.includes(error.options.url.hostname)); t.is(error.options.url.pathname, '/'); + t.is(error.code, 'ERR_BODY_PARSE_FAILURE'); }); test('wraps parsing errors', withServer, async (t, server, got) => { @@ -100,6 +101,7 @@ test('wraps parsing errors', withServer, async (t, server, got) => { const error = await t.throwsAsync(got({responseType: 'json'}), {instanceOf: got.ParseError}); t.true(error.message.includes(error.options.url.hostname)); t.is(error.options.url.pathname, '/'); + t.is(error.code, 'ERR_BODY_PARSE_FAILURE'); }); test('parses non-200 responses', withServer, async (t, server, got) => { @@ -136,6 +138,7 @@ test('parse errors have `response` property', withServer, async (t, server, got) t.is(error.response.statusCode, 200); t.is(error.response.body, '/'); + t.is(error.code, 'ERR_BODY_PARSE_FAILURE'); }); test('sets correct headers', withServer, async (t, server, got) => { @@ -186,6 +189,7 @@ test('shortcuts throw ParseErrors', withServer, async (t, server, got) => { await t.throwsAsync(got('').json(), { instanceOf: ParseError, + code: 'ERR_BODY_PARSE_FAILURE', message: /^Unexpected token o in JSON at position 1 in/ }); });