diff --git a/source/core/index.ts b/source/core/index.ts index 7c43f4fcd..a96d8035a 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -1222,7 +1222,7 @@ export class RequestError extends Error { this.timings = this.request?.timings; // Recover the original stacktrace - if (!is.undefined(error.stack)) { + if (is.string(error.stack) && is.string(this.stack)) { const indexOfMessage = this.stack.indexOf(this.message) + this.message.length; const thisStackTrace = this.stack.slice(indexOfMessage).split('\n').reverse(); const errorStackTrace = error.stack.slice(error.stack.indexOf(error.message!) + error.message!.length).split('\n').reverse(); diff --git a/test/error.ts b/test/error.ts index faebb37fd..9a3161494 100644 --- a/test/error.ts +++ b/test/error.ts @@ -3,6 +3,8 @@ import net = require('net'); import http = require('http'); import stream = require('stream'); import test from 'ava'; +import getStream = require('get-stream'); +import is from '@sindresorhus/is'; import got, {RequestError, HTTPError, TimeoutError} from '../source'; import withServer from './helpers/with-server'; @@ -261,3 +263,70 @@ test.skip('the old stacktrace is recovered', async t => { // the second `at get` points to the real cause. t.not(error.stack!.indexOf('at get'), error.stack!.lastIndexOf('at get')); }); + +test.serial('custom stack trace', withServer, async (t, _server, got) => { + const ErrorCaptureStackTrace = Error.captureStackTrace; + + const enable = () => { + Error.captureStackTrace = (target: {stack: any}) => { + target.stack = [ + 'line 1', + 'line 2' + ]; + }; + }; + + const disable = () => { + Error.captureStackTrace = ErrorCaptureStackTrace; + }; + + // Node.js default behavior + { + const stream = got.stream(''); + stream.destroy(new Error('oh no')); + + const caught = await t.throwsAsync(getStream(stream)); + t.is(is(caught.stack), 'string'); + } + + // Passing a custom error + { + enable(); + const error = new Error('oh no'); + disable(); + + const stream = got.stream(''); + stream.destroy(error); + + const caught = await t.throwsAsync(getStream(stream)); + t.is(is(caught.stack), 'string'); + } + + // Custom global behavior + { + enable(); + const error = new Error('oh no'); + + const stream = got.stream(''); + stream.destroy(error); + + const caught = await t.throwsAsync(getStream(stream)); + t.is(is(caught.stack), 'Array'); + + disable(); + } + + // Passing a default error that needs some processing + { + const error = new Error('oh no'); + enable(); + + const stream = got.stream(''); + stream.destroy(error); + + const caught = await t.throwsAsync(getStream(stream)); + t.is(is(caught.stack), 'Array'); + + disable(); + } +});