Skip to content

fix(node/browser): Always use constructor to get error type #8161

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/browser/src/eventbuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export function exceptionFromError(stackParser: StackParser, ex: Error): Excepti
const frames = parseStackFrames(stackParser, ex);

const exception: Exception = {
type: ex && ex.name,
// This is necessary in order to get the name of user-defined errors which subclass `Error`
type: ex && ex.constructor.name,
value: extractMessage(ex),
};

Expand Down
24 changes: 23 additions & 1 deletion packages/browser/test/unit/eventbuilder.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Client } from '@sentry/types';

import { defaultStackParser } from '../../src';
import { eventFromPlainObject } from '../../src/eventbuilder';
import { eventFromPlainObject, exceptionFromError } from '../../src/eventbuilder';

jest.mock('@sentry/core', () => {
const original = jest.requireActual('@sentry/core');
Expand Down Expand Up @@ -62,3 +62,25 @@ describe('eventFromPlainObject', () => {
});
});
});

describe('exceptionFromError ', () => {
it('correctly reads error type and value from built-in `Error` subclass', () => {
const exceptionJSON = exceptionFromError(() => [], new TypeError("Expected type 'ChewToy', got type 'Shoe'"));

expect(exceptionJSON).toEqual({
type: 'TypeError',
value: "Expected type 'ChewToy', got type 'Shoe'",
});
});

it('correctly reads error type and value from user-defined `Error` subclass', () => {
class DidNotFetch extends Error {}

const exceptionJSON = exceptionFromError(() => [], new DidNotFetch("Failed to fetch requested object: 'ball'"));

expect(exceptionJSON).toEqual({
type: 'DidNotFetch',
value: "Failed to fetch requested object: 'ball'",
});
});
});
6 changes: 6 additions & 0 deletions packages/browser/test/unit/helper/error-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Convert an Error-like object into something TS recognizes as an `Error`, getting around read-only properties
export function makeMockError(obj: { [key: string]: any; message: string; name: string; stack?: string }): Error {
const anyObj = obj as any;
anyObj.constructor = { name: obj.name };
return anyObj as Error;
}
35 changes: 18 additions & 17 deletions packages/browser/test/unit/tracekit/chromium.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { exceptionFromError } from '../../../src/eventbuilder';
import { defaultStackParser as parser } from '../../../src/stack-parsers';
import { makeMockError } from '../helper/error-object';

describe('Tracekit - Chrome Tests', () => {
it('should parse Chrome error with no location', () => {
const NO_LOCATION = { message: 'foo', name: 'bar', stack: 'error\n at Array.forEach (native)' };
const ex = exceptionFromError(parser, NO_LOCATION);
const ex = exceptionFromError(parser, makeMockError(NO_LOCATION));

expect(ex).toEqual({
value: 'foo',
Expand All @@ -26,7 +27,7 @@ describe('Tracekit - Chrome Tests', () => {
' at http://path/to/file.js:24:4',
};

const ex = exceptionFromError(parser, CHROME_15);
const ex = exceptionFromError(parser, makeMockError(CHROME_15));

expect(ex).toEqual({
value: "Object #<Object> has no method 'undef'",
Expand All @@ -53,7 +54,7 @@ describe('Tracekit - Chrome Tests', () => {
' at I.e.fn.(anonymous function) [as index] (http://localhost:8080/file.js:10:3651)',
};

const ex = exceptionFromError(parser, CHROME_36);
const ex = exceptionFromError(parser, makeMockError(CHROME_36));

expect(ex).toEqual({
value: 'Default error',
Expand Down Expand Up @@ -99,7 +100,7 @@ describe('Tracekit - Chrome Tests', () => {
' at TESTTESTTEST.proxiedMethod(webpack:///./~/react-proxy/modules/createPrototypeProxy.js?:44:30)',
};

const ex = exceptionFromError(parser, CHROME_XX_WEBPACK);
const ex = exceptionFromError(parser, makeMockError(CHROME_XX_WEBPACK));

expect(ex).toEqual({
value: "Cannot read property 'error' of undefined",
Expand Down Expand Up @@ -152,7 +153,7 @@ describe('Tracekit - Chrome Tests', () => {
'at http://localhost:8080/file.js:31:13\n',
};

const ex = exceptionFromError(parser, CHROME_48_EVAL);
const ex = exceptionFromError(parser, makeMockError(CHROME_48_EVAL));

expect(ex).toEqual({
value: 'message string',
Expand Down Expand Up @@ -184,7 +185,7 @@ describe('Tracekit - Chrome Tests', () => {
' at n.handle (blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379:7:2863)',
};

const ex = exceptionFromError(parser, CHROME_48_BLOB);
const ex = exceptionFromError(parser, makeMockError(CHROME_48_BLOB));

expect(ex).toEqual({
value: 'Error: test',
Expand Down Expand Up @@ -247,7 +248,7 @@ describe('Tracekit - Chrome Tests', () => {
at examplescheme://examplehost/cd351f7250857e22ceaa.worker.js:70179:15`,
};

const ex = exceptionFromError(parser, CHROMIUM_EMBEDDED_FRAMEWORK_CUSTOM_SCHEME);
const ex = exceptionFromError(parser, makeMockError(CHROMIUM_EMBEDDED_FRAMEWORK_CUSTOM_SCHEME));

expect(ex).toEqual({
value: 'message string',
Expand Down Expand Up @@ -277,7 +278,7 @@ describe('Tracekit - Chrome Tests', () => {
at http://localhost:5000/test:24:7`,
};

const ex = exceptionFromError(parser, CHROME73_NATIVE_CODE_EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(CHROME73_NATIVE_CODE_EXCEPTION));

expect(ex).toEqual({
value: 'test',
Expand Down Expand Up @@ -310,7 +311,7 @@ describe('Tracekit - Chrome Tests', () => {
at http://localhost:5000/:50:19`,
};

const ex = exceptionFromError(parser, CHROME73_EVAL_EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(CHROME73_EVAL_EXCEPTION));

expect(ex).toEqual({
value: 'bad',
Expand Down Expand Up @@ -342,7 +343,7 @@ describe('Tracekit - Chrome Tests', () => {
at test (http://localhost:5000/:33:23)`,
};

const ex = exceptionFromError(parser, CHROME_109_ASYNC_URL);
const ex = exceptionFromError(parser, makeMockError(CHROME_109_ASYNC_URL));

expect(ex).toEqual({
value: 'bad',
Expand All @@ -368,7 +369,7 @@ describe('Tracekit - Chrome Tests', () => {
at Global code (http://localhost:5000/test:24:7)`,
};

const ex = exceptionFromError(parser, EDGE44_NATIVE_CODE_EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(EDGE44_NATIVE_CODE_EXCEPTION));

expect(ex).toEqual({
value: 'test',
Expand Down Expand Up @@ -401,7 +402,7 @@ describe('Tracekit - Chrome Tests', () => {
at Anonymous function (http://localhost:5000/:50:8)`,
};

const ex = exceptionFromError(parser, EDGE44_EVAL_EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(EDGE44_EVAL_EXCEPTION));

expect(ex).toEqual({
value: 'aha',
Expand Down Expand Up @@ -437,7 +438,7 @@ describe('Tracekit - Chrome Tests', () => {
at TESTTESTTEST.someMethod (C:\\Users\\user\\path\\to\\file.js:295:108)`,
};

const ex = exceptionFromError(parser, CHROME_ELECTRON_RENDERER);
const ex = exceptionFromError(parser, makeMockError(CHROME_ELECTRON_RENDERER));

expect(ex).toEqual({
value: "Cannot read property 'error' of undefined",
Expand Down Expand Up @@ -469,7 +470,7 @@ describe('Tracekit - Chrome Tests', () => {
at commitLayoutEffects (react-dom.development.js?f8c1:23426:1)`,
};

const ex = exceptionFromError(parser, EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(EXCEPTION));

expect(ex).toEqual({
value: 'aha',
Expand Down Expand Up @@ -535,7 +536,7 @@ describe('Tracekit - Chrome Tests', () => {
at Array.reduce(<anonymous>)`,
};

const ex = exceptionFromError(parser, EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(EXCEPTION));

expect(ex).toEqual({
value: 'aha',
Expand Down Expand Up @@ -582,7 +583,7 @@ describe('Tracekit - Chrome Tests', () => {
at more (http://localhost:5000/(some)/(thing)/index.html:25:7)`,
};

const ex = exceptionFromError(parser, CHROME_BRACES_URL);
const ex = exceptionFromError(parser, makeMockError(CHROME_BRACES_URL));

expect(ex).toEqual({
value: 'bad',
Expand Down Expand Up @@ -620,7 +621,7 @@ describe('Tracekit - Chrome Tests', () => {
at http://localhost:5000/:50:19`,
};

const ex = exceptionFromError(parser, LONG_FRAME);
const ex = exceptionFromError(parser, makeMockError(LONG_FRAME));

expect(ex).toEqual({
value: 'bad',
Expand Down
21 changes: 11 additions & 10 deletions packages/browser/test/unit/tracekit/firefox.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { exceptionFromError } from '../../../src/eventbuilder';
import { defaultStackParser as parser } from '../../../src/stack-parsers';
import { makeMockError } from '../helper/error-object';

describe('Tracekit - Firefox Tests', () => {
it('should parse Firefox 3 error', () => {
Expand All @@ -19,7 +20,7 @@ describe('Tracekit - Firefox Tests', () => {
'',
};

const ex = exceptionFromError(parser, FIREFOX_3);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_3));

expect(ex).toEqual({
value: 'this.undef is not a function',
Expand Down Expand Up @@ -55,7 +56,7 @@ describe('Tracekit - Firefox Tests', () => {
'',
};

const ex = exceptionFromError(parser, FIREFOX_7);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_7));

expect(ex).toEqual({
value: 'bar',
Expand Down Expand Up @@ -87,7 +88,7 @@ describe('Tracekit - Firefox Tests', () => {
lineNumber: 48,
};

const ex = exceptionFromError(parser, FIREFOX_14);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_14));

expect(ex).toEqual({
value: 'x is null',
Expand Down Expand Up @@ -116,7 +117,7 @@ describe('Tracekit - Firefox Tests', () => {
columnNumber: 12,
};

const ex = exceptionFromError(parser, FIREFOX_31);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_31));

expect(ex).toEqual({
value: 'Default error',
Expand Down Expand Up @@ -151,7 +152,7 @@ describe('Tracekit - Firefox Tests', () => {
result: 2147500037,
};

const ex = exceptionFromError(parser, FIREFOX_44_NS_EXCEPTION);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_44_NS_EXCEPTION));

expect(ex).toEqual({
value: 'No error message',
Expand Down Expand Up @@ -186,7 +187,7 @@ describe('Tracekit - Firefox Tests', () => {
name: 'TypeError',
};

const ex = exceptionFromError(parser, FIREFOX_50_RESOURCE_URL);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_50_RESOURCE_URL));

expect(ex).toEqual({
value: 'this.props.raw[this.state.dataSource].rows is undefined',
Expand Down Expand Up @@ -234,7 +235,7 @@ describe('Tracekit - Firefox Tests', () => {
'@http://localhost:8080/file.js:33:9',
};

const ex = exceptionFromError(parser, FIREFOX_43_EVAL);
const ex = exceptionFromError(parser, makeMockError(FIREFOX_43_EVAL));

expect(ex).toEqual({
value: 'message string',
Expand All @@ -260,7 +261,7 @@ describe('Tracekit - Firefox Tests', () => {
@http://localhost:5000/test:24:7`,
};

const stacktrace = exceptionFromError(parser, FIREFOX66_NATIVE_CODE_EXCEPTION);
const stacktrace = exceptionFromError(parser, makeMockError(FIREFOX66_NATIVE_CODE_EXCEPTION));

expect(stacktrace).toEqual({
value: 'test',
Expand Down Expand Up @@ -290,7 +291,7 @@ describe('Tracekit - Firefox Tests', () => {
@http://localhost:5000/:50:19`,
};

const stacktrace = exceptionFromError(parser, FIREFOX66_EVAL_EXCEPTION);
const stacktrace = exceptionFromError(parser, makeMockError(FIREFOX66_EVAL_EXCEPTION));

expect(stacktrace).toEqual({
value: 'aha',
Expand Down Expand Up @@ -324,7 +325,7 @@ describe('Tracekit - Firefox Tests', () => {
name: 'TypeError',
};

const stacktrace = exceptionFromError(parser, FIREFOX_FILE_IN_IDENTIFIER);
const stacktrace = exceptionFromError(parser, makeMockError(FIREFOX_FILE_IN_IDENTIFIER));

expect(stacktrace).toEqual({
stacktrace: {
Expand Down
7 changes: 4 additions & 3 deletions packages/browser/test/unit/tracekit/ie.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { exceptionFromError } from '../../../src/eventbuilder';
import { defaultStackParser as parser } from '../../../src/stack-parsers';
import { makeMockError } from '../helper/error-object';

describe('Tracekit - IE Tests', () => {
it('should parse IE 10 error', () => {
Expand All @@ -15,7 +16,7 @@ describe('Tracekit - IE Tests', () => {
number: -2146823281,
};

const ex = exceptionFromError(parser, IE_10);
const ex = exceptionFromError(parser, makeMockError(IE_10));

// TODO: func should be normalized
expect(ex).toEqual({
Expand Down Expand Up @@ -44,7 +45,7 @@ describe('Tracekit - IE Tests', () => {
number: -2146823281,
};

const ex = exceptionFromError(parser, IE_11);
const ex = exceptionFromError(parser, makeMockError(IE_11));

// TODO: func should be normalized
expect(ex).toEqual({
Expand Down Expand Up @@ -73,7 +74,7 @@ describe('Tracekit - IE Tests', () => {
number: -2146823279,
};

const ex = exceptionFromError(parser, IE_11_EVAL);
const ex = exceptionFromError(parser, makeMockError(IE_11_EVAL));

expect(ex).toEqual({
value: "'getExceptionProps' is undefined",
Expand Down
5 changes: 3 additions & 2 deletions packages/browser/test/unit/tracekit/misc.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { exceptionFromError } from '../../../src/eventbuilder';
import { defaultStackParser as parser } from '../../../src/stack-parsers';
import { makeMockError } from '../helper/error-object';

describe('Tracekit - Misc Tests', () => {
it('should parse PhantomJS 1.19 error', () => {
Expand All @@ -12,7 +13,7 @@ describe('Tracekit - Misc Tests', () => {
' at foo (http://path/to/file.js:4283)\n' +
' at http://path/to/file.js:4287',
};
const ex = exceptionFromError(parser, PHANTOMJS_1_19);
const ex = exceptionFromError(parser, makeMockError(PHANTOMJS_1_19));

expect(ex).toEqual({
value: 'bar',
Expand Down Expand Up @@ -47,7 +48,7 @@ describe('Tracekit - Misc Tests', () => {
' at playTimer.current(./app/components/replays/replayContext.tsx:397:62)\n' +
' at sentryWrapped(../node_modules/@sentry/browser/esm/helpers.js:90:17)',
};
const ex = exceptionFromError(parser, SECURITY_ERROR);
const ex = exceptionFromError(parser, makeMockError(SECURITY_ERROR));

expect(ex).toEqual({
type: 'SecurityError',
Expand Down
Loading