GithubHelp home page GithubHelp logo

serialize-error's Introduction

serialize-error

Serialize/deserialize an error into a plain object

Useful if you for example need to JSON.stringify() or process.send() the error.

Install

npm install serialize-error

Usage

import {serializeError, deserializeError} from 'serialize-error';

const error = new Error('πŸ¦„');

console.log(error);
//=> [Error: πŸ¦„]

const serialized = serializeError(error);

console.log(serialized);
//=> {name: 'Error', message: 'πŸ¦„', stack: 'Error: πŸ¦„\n    at Object.<anonymous> …'}

const deserialized = deserializeError(serialized);

console.log(deserialized);
//=> [Error: πŸ¦„]

Error constructors

When a serialized error with a known name is encountered, it will be deserialized using the corresponding error constructor, while unknown error names will be deserialized as regular errors:

import {deserializeError} from 'serialize-error';

const known = deserializeError({
	name: 'TypeError',
	message: 'πŸ¦„'
});

console.log(known);
//=> [TypeError: πŸ¦„] <-- Still a TypeError

const unknown = deserializeError({
	name: 'TooManyCooksError',
	message: 'πŸ¦„'
});

console.log(unknown);
//=> [Error: πŸ¦„] <-- Just a regular Error

The list of known errors can be extended globally. This also works if serialize-error is a sub-dependency that's not used directly.

import {errorConstructors} from 'serialize-error';
import {MyCustomError} from './errors.js'

errorConstructors.set('MyCustomError', MyCustomError)

Warning: Only simple and standard error constructors are supported, like new MyCustomError(message). If your error constructor requires a second parameter or does not accept a string as first parameter, adding it to this map will break the deserialization.

API

serializeError(value, options?)

Serialize an Error object into a plain object.

  • Non-error values are passed through.
  • Custom properties are preserved.
  • Non-enumerable properties are kept non-enumerable (name, message, stack).
  • Enumerable properties are kept enumerable (all properties besides the non-enumerable ones).
  • Buffer properties are replaced with [object Buffer].
  • Circular references are handled.
  • If the input object has a .toJSON() method, then it's called instead of serializing the object's properties.
  • It's up to .toJSON() implementation to handle circular references and enumerability of the properties.

value

Type: Error | unknown

toJSON implementation examples

import {serializeError} from 'serialize-error';

class ErrorWithDate extends Error {
	constructor() {
		super();
		this.date = new Date();
	}
}

const error = new ErrorWithDate();

serializeError(error);
// => {date: '1970-01-01T00:00:00.000Z', name, message, stack}
import {serializeError} from 'serialize-error';

const error = new Error('Unicorn');

error.horn = {
	toJSON() {
		return 'x';
	}
};

serializeError(error);
// => {horn: 'x', name, message, stack}

deserializeError(value, options?)

Deserialize a plain object or any value into an Error object.

  • Error objects are passed through.
  • Objects that have at least a message property are interpreted as errors.
  • All other values are wrapped in a NonError error.
  • Custom properties are preserved.
  • Non-enumerable properties are kept non-enumerable (name, message, stack, cause).
  • Enumerable properties are kept enumerable (all properties besides the non-enumerable ones).
  • Circular references are handled.
  • Native error constructors are preserved (TypeError, DOMException, etc) and more can be added.

value

Type: {message: string} | unknown

options

Type: object

maxDepth

Type: number
Default: Number.POSITIVE_INFINITY

The maximum depth of properties to preserve when serializing/deserializing.

import {serializeError} from 'serialize-error';

const error = new Error('πŸ¦„');
error.one = {two: {three: {}}};

console.log(serializeError(error, {maxDepth: 1}));
//=> {name: 'Error', message: 'πŸ¦„', one: {}}

console.log(serializeError(error, {maxDepth: 2}));
//=> {name: 'Error', message: 'πŸ¦„', one: { two: {}}}

useToJSON

Type: boolean
Default: true

Indicate whether to use a .toJSON() method if encountered in the object. This is useful when a custom error implements its own serialization logic via .toJSON() but you prefer to not use it.

isErrorLike(value)

Predicate to determine whether a value looks like an error, even if it's not an instance of Error. It must have at least the name, message, and stack properties.

import {isErrorLike} from 'serialize-error';

const error = new Error('πŸ¦„');
error.one = {two: {three: {}}};

isErrorLike({
	name: 'DOMException',
	message: 'It happened',
	stack: 'at foo (index.js:2:9)',
});
//=> true

isErrorLike(new Error('πŸ¦„'));
//=> true

isErrorLike(serializeError(new Error('πŸ¦„'));
//=> true

isErrorLike({
	name: 'Bluberricious pancakes',
	stack: 12,
	ingredients: 'Blueberry',
});
//=> false

serialize-error's People

Contributors

bendingbender avatar braco avatar chrisguttandin avatar coreyfarrell avatar fregante avatar hec10r avatar jackple avatar jamestalmage avatar kikobeats avatar ltburruel avatar misozask avatar ofryl avatar richienb avatar rj-david avatar sindresorhus avatar supernavix avatar victorpuga avatar vladimiry avatar weakky avatar wodka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

serialize-error's Issues

Error subclasses are lost when deserializing

const {serializeError, deserializeError} = require('serialize-error');

class MyError extends Error {
    constructor(message) {
        super(message);
        this.name = 'MyError'
    }
}

const original = new MyError('This is my error');
const serialized = serializeError(original);
const deserialized = deserializeError(serialized);

original will be a MyError but deserialized will be a plain Error

Live example: https://runkit.com/embed/f46ewcgxyxqg

I think deserializeError should support custom error types, if passed as an argument (so they are known and explicit):

const deserialized = deserializeError(serialized, {constructors: [MyError, BusinessError, ErrorError]});

Native errors also should be automatically supported, like TypeError

change in v5: `const {inspect} = require('util');` can lead to web bundle bloat

This was added in v5 for deserializeError:

const {inspect} = require('util');

This adds about 8KB to web bundles, which can be significant, especially in projects not using deserializeError.

Possible solutions:

  • Not worry about 8KB and tell developers to use v4 if bundle size is of concern.
  • Separate serializeError into a separate file that can be imported without the deserializeError code.
  • Separate deserializeError into a new module.
  • Publish code with ES6-style import, add sideEffects: false to the package.json to enable tree shaking in the module.
  • Other ideas?

Handle custom error constructors with incompatible signatures

This is the native constructor:

class Error {
	constructor(message, options) {}
}

This is custom constructor that is compatible with serialize-error:

class CustomError extends Error {
	constructor(message) {
		super(message)
	}
}

This is custom constructor that is not compatible with serialize-error:

class CustomError extends Error {
	constructor(message, requiredParameter) {
		super(message)
		if (!requiredParameter) {
			throw new Error('requiredParameter missing')
		}
	}
}

Two-part solution:

  1. catch errors on creation and use the default Error instead, here:
    to_: new Error(),
  2. add support for custom constructors, e.g.
    errorConstructors.set('CustomError', message => new CustomError(message, {}))

Jest encounters `SyntaxError: Cannot use import statement outside a module` while trying to test code that uses `serialize-error`

I recently added serialize-error to my project. Using the code works great but while trying to test it, jest encounters an issue and fails me:

  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     β€’ If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     β€’ If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     β€’ To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     β€’ If you need a custom transformation specify a "transform" option in your config.
     β€’ If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /node_modules/serialize-error/index.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import errorConstructors from './error-constructors.js';
                                                                                      ^^^^^^

    SyntaxError: Cannot use import statement outside a module


    > 3 | import { serializeError } from 'serialize-error';
        | ^
      4 | import { Repository } from 'typeorm';


      at Runtime.createScriptFromCode (../../../node_modules/jest-runtime/build/index.js:1728:14)
...

I'm using version 11 of serialize-error

Is there anything I can do to make jest work with the lib?
Thanks!

Optionally ignore `toJSON`

serialize-error automatically uses toJSON:

serialize-error/index.js

Lines 61 to 63 in 855fe3d

if (typeof from.toJSON === 'function' && from[toJsonWasCalled] !== true) {
return toJSON(from);
}

Unfortunately this introduces two unwanted behaviors:

  • code can be executed by passing {toJSON() { /*h4xor*/ }}
  • serialization is decided by the object owner, with which we might disagree (e.g. it might exclude arbitrary properties)

Possible solution

serializeError(error, {toJSON: false})

Unknown object type "asyncfunction"

I love serize error but all of a sudden today (in the cloud) I started getting the following error in my app. Since serialize-error seems to be the only lib using object-hash, I decided to remove serialize-error.. and everything was ok again.. any idea why it happens ?

/var/www/node_modules/object-hash/index.js:218
          throw new Error('Unknown object type "' + objType + '"');
                ^

Error: Unknown object type "asyncfunction"
    at Object._object (/var/www/node_modules/object-hash/index.js:218:17)
    at Object._function (/var/www/node_modules/object-hash/index.js:319:14)
    at Object.dispatch (/var/www/node_modules/object-hash/index.js:185:30)
    at /var/www/node_modules/object-hash/index.js:246:18
    at Array.forEach (<anonymous>)
    at Object._object (/var/www/node_modules/object-hash/index.js:242:21)
    at Object._function (/var/www/node_modules/object-hash/index.js:319:14)
    at Object.dispatch (/var/www/node_modules/object-hash/index.js:185:30)
    at /var/www/node_modules/object-hash/index.js:246:18
    at Array.forEach (<anonymous>)

Skip non-enumerable properties in destroyCircular

I'm using AVA to test React components and running into a bizarre issue. In some circumstances, when I create a react component and the test fails, the error object has a reference to the react component somewhere deep inside error.powerAssertContext. I'm not sure how it gets the reference; must be getting it because of the espower babel transform somehow.

In React version 15.0.0-rc.2 they added warnings when you access props.key or props.ref. This is implemented with a getter property that is not enumerable and throws an error if accessed.

Now, because my error object references a React component, I end up with a very large meaningless stack trace when my tests fail.

In destroyCircular on line 34, you're using Object.getOwnPropertyNames and accessing each property it returns. The problem for me is that when you access the key or ref properties, React throws an error.

I can't think of any reason you would want to serialize non-enumerable properties, so I think the best solution here would be to use Object.keys instead of Object.getOwnPropertyNames. If that's not an option, could we at least wrap it in a try/catch and skip the properties that throw errors?

Add `isErrorObject` type guard / function

What do you think about exporting a type-checker function (like Array.isArray) that doubles as a type guard?

export function isErrorObject(error: unknown): error is ErrorObject {
  return error && typeof error === 'object' && 'message' in error;
}

Consider Object.toJSON() method

Some program defines custom error type (e.g. MyError) and the custom error type also may have the toJSON() method, that would be used in JSON.stringify(). In such reason, I recommend serializeError function to inspects the toJSON() method first.

class MyError extends Error
{
    public readonly code: number;
    public readonly type: string;
    
    public constructor(code: number, type: string, message: string);
    public toJSON(): object
    {
        return {
            name: this.name, stack: this.stack, message: this.message,
            code: this.code, type: this.type
        };
    }
}

v7 doesn't work as described

The breaking change in v7 turned the example from the README.md useless:

const {serializeError, deserializeError} = require('serialize-error');

const error = new Error('πŸ¦„');

console.log(error);
//=> [Error: πŸ¦„]

const serialized = serializeError(error)

console.log(serialized);
//=> {name: 'Error', message: 'πŸ¦„', stack: 'Error: πŸ¦„\n    at Object.<anonymous> …'}

const deserialized = deserializeError(serialized);
//=> [Error: πŸ¦„]

The serialized error looks just like an empty object now, and is missing the name, message, and stack properties on the console. Actually, for me this was the most useful feature, and hence v7 is a huge step into the wrong direction.

I understand that you wanted to change it this way to keep things consistent – however, is there a chance to make this configurable?

ESBuild error when importing serialize-error

My esbuild doesnt resolve serialize-error node package.

➜  cognito-triggers source ~/.aws/lu && sam build && sam deploy --config-file samconfig.dev.toml

Building codeuri: .../cognito-triggers/lambdas runtime: nodejs18.x metadata: {'BuildMethod': 'esbuild', 'BuildProperties': {'Format': 'cjs', 'Minify': False, 'Target':
'es2022', 'EntryPoints': ['src/trigger-post-confirmation.ts']}} architecture: x86_64 functions: CognitoTriggerPostConfirmationFunction
Running NodejsNpmEsbuildBuilder:CopySource
Running NodejsNpmEsbuildBuilder:NpmInstall
Running NodejsNpmEsbuildBuilder:EsbuildBundle

Build Failed
Error: NodejsNpmEsbuildBuilder:EsbuildBundle - Esbuild Failed: ✘ [ERROR] Could not resolve "serialize-error"

    src/trigger-post-confirmation.ts:2:31:
      2 β”‚ import { serializeError } from "serialize-error";
        β•΅                                ~~~~~~~~~~~~~~~~~

  You can mark the path "serialize-error" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.

1 error

Other package are working fine. Why is serialize-error different?

Consider making common error properties non enumerable

Description

Native JS errors don't have its properties enumerable. What this means is that the name, stack and message properties do not appear when console.logging errors.

Notice the name and message property appearing below.

image

Proposal

To get deserialized errors closer to the native ones, when deserializing the error, we could use Object.defineProperty instead of a direct assignment to make the properties not enumerable. eg: here https://github.com/sindresorhus/serialize-error/blob/master/index.js#L54

for (const property of commonProperties) {
	if (typeof from[property] === 'string') {
        Object.defineProperty(to, property, {
          value: from[property]
        })
	}
}

I'm happy to make a PR if you agree with the proposal!

Thanks for your awesome work across the Node community ❀️

Wrong typescript definition for serializeError

If I am understanding the TypeScript correctly,

https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables

https://github.com/sindresorhus/serialize-error/blob/main/index.js#L160

If the value input is function,

Should the type of return value must be also function as well?

If we still want to convert function value to string,

The type definition for input & return value should be any or unknown since they don't share the same type.

export function serializeError(error: any, options?: Options): any

Visibility of undefined values in the objects

I want to keep undefined as the presented value in the objects.
Is it possible? Right now all of the undefined parameters are filtered out from the output.

Right now output looks like this:

data: {
  b: 4
}

Expected:

data: {
  a: undefined,
  b: 4,
  c: undefined
}

Type definitions not being imported

I'm getting the following type error when trying to use this package as:

import serializeError from "serialize-error";
const serializedError = serializeError(error);
This expression is not callable.
  Type 'typeof import("/Users/farah/{PATH}/node_modules/serialize-error/index")' has no call signatures.ts(2349)
Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/Users/farah/{PATH}/node_modules/serialize-error/index"' has no compatible call signatures.ts(234

tsconfig.js

{
  "compilerOptions": {
    "target": "es2015",
    "declaration": true,
    "lib": ["esnext", "dom"],
    "baseUrl": "src",
    "allowJs": true,
    "rootDir": "src",
    "skipLibCheck": true,
    "importHelpers": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noUnusedLocals": false,
    "noEmit": true,
    "jsx": "preserve"
  },
  "typeAcquisition": {
    "enable": true
  },
  "exclude": ["node_modules", "build", "scripts"],
  "include": ["src", "types"]
}

Have tried both v6 and v7.

`option` to treat undefined as null

Hello, can we add an option to the serializeError function so that it does not error out when an undefined is encountered?

Error: Error serializing `.error.status` returned from `getServerSideProps`.
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.

Sometimes we are dealing with 3rd party errors so we cannot modify them.

Fails to serialize errors containing a http.ClientRequest

I'm still trying to make a reduced, reproducible case for this one. However, I thought someone might have some ideas about what's going on.

Running some tests with ava, which performs an HTTP request. At some point it encounters an error that should be sent from the worker to the master process, which calls serialize-error to be able to safely pass the data. After a while it crashes the node process with the following info:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory

<--- Last few GCs --->

   81235 ms: Mark-sweep 1397.3 (1458.0) -> 1397.0 (1458.0) MB, 1140.1 / 0 ms [allocation failure] [GC in old space requested].
   82397 ms: Mark-sweep 1397.0 (1458.0) -> 1397.0 (1458.0) MB, 1161.6 / 0 ms [allocation failure] [GC in old space requested].
   83542 ms: Mark-sweep 1397.0 (1458.0) -> 1397.0 (1458.0) MB, 1144.9 / 0 ms [last resort gc].
   84681 ms: Mark-sweep 1397.0 (1458.0) -> 1397.0 (1458.0) MB, 1139.0 / 0 ms [last resort gc].


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x291f70db4629 <JS Object>
    1: destroyCircular(aka destroyCircular) [/home/espenh/webdev/crown/node_modules/serialize-error/index.js:~20] [pc=0xd42dc460314] (this=0x291f70d041b9 <undefined>,from=0x1f20d233a839 <an Object with map 0x32ba4fda171>,seen=0x2c9b3e0260f1 <JS Array[25]>)
    2: /* anonymous */(aka /* anonymous */) [/home/espenh/webdev/crown/node_modules/serialize-error/index.js:47] [pc=0xd42dc44cb31] (this=0x...

With some trial and error, I've reduced the problem down
props.error.powerAssertContext.args[0].events[0].value.request. If I exclude this property from the serialization process, it works.

Right before the process fails, it outputs:

(node) Server.connections property is deprecated. Use Server.getConnections method instead.

Any ideas on what is going on here? It seems like there might be a circular reference within the request somehow, that is not being picked up by destroyCircular, properly. I can't seem to easily reproduce it in the tests for serialize-error, however.

ERR_REQUIRE_ESM

I'm using (as per the doc) :

import { serializeError } from 'serialize-error';

When executing my script, it crashes with this error :

Error [ERR_REQUIRE_ESM]: require() of ES Module /media/sf_NPL/core/node_modules/serialize-error/index.js from /media/sf_NPL/core/imports/manager/imports/log.ts not supported.
Instead change the require of index.js in /media/sf_NPL/core/imports/manager/imports/log.ts to a dynamic import() which is available in all CommonJS modules.
    at Object.require.extensions.<computed> [as .js] (/home/jer/.nvm/versions/node/v16.20.2/lib/node_modules/ts-node/dist/index.js:851:20)
    at Object.<anonymous> (/media/sf_NPL/core/imports/manager/imports/log.ts:28:27)
    at Module.m._compile (/home/jer/.nvm/versions/node/v16.20.2/lib/node_modules/ts-node/dist/index.js:857:29)
    at Object.require.extensions.<computed> [as .ts] (/home/jer/.nvm/versions/node/v16.20.2/lib/node_modules/ts-node/dist/index.js:859:16)
    at new Manager (/media/sf_NPL/core/imports/manager/index.ts:15:20)
    at /media/sf_NPL/core/index.ts:80:19 {
  code: 'ERR_REQUIRE_ESM'
}

I'm getting the same error if I use instead :

const serializeError = require('serialize-error')

I write Typescript code which is then executed with ts-node myscript.ts.

Versions :
node 16.20.2
serialize-error ^11.0.1
ts-node 10.9.1

The output is not JSON.stringify-safe

import {serializeError} from 'serialize-error'

const err = new Error
err.timestamp = process.hrtime.bigint()

JSON.stringify(serializeError(err))

// Uncaught TypeError: Do not know how to serialize a BigInt
//     at JSON.stringify (<anonymous>)

The fix seems simple if doing something akin to buffers (though the value could be embedded). Am I missing something? If fixed by a PR, could it be backported no a non-ESM version?

Nested errors are still not serialized

I was thrown off by this test:

serialize-error/test.js

Lines 129 to 136 in 6f6102f

test('should serialize nested errors', t => {
const error = new Error('outer error');
error.innerError = new Error('inner error');
const serialized = serializeError(error);
t.is(serialized.message, 'outer error');
t.is(serialized.innerError.message, 'inner error');
});

This is incorrect. The test is not testing whether the property was serialized, but whether it exists. The nested error is there still as an Error instance.

`deserializeError` serializes/destroys properties unnecessarily

This module is set up in a weird way. destroyCircular is called both when serializing and deserializing and it's doing two separate things:

The second part part is particularly problematic because during deserialization, this module is potentially dropping functions and all other "supported" types like buffer. This is unexpected:

deserializeError({
	name: 'Error'
	message: 'πŸ’©',
	data: new Buffer([1,2,3])
});
// [Error: πŸ’©] with `data` property of value "[object Buffer]"

I'd expect the string "[object Buffer]" when serializing an error, not during deserialization.

Use stricter type for JsonObject

JsonObject that comes from 'type-fest' (annoyingly) includes undefined as valid value, which is not the case for json.

A better definition would be:

type JsonObject = { [key: string]: JsonValue, };
type JsonValue = JsonObject | JsonValue[] | boolean | number | string | null;

Jest encountered an unexpected token

I'm running my tests using Jest in with TypeScript, but it throws:

`
Jest encountered an unexpected token

This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

Here's what you can do:
 β€’ To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
 β€’ If you need a custom transformation specify a "transform" option in your config.
 β€’ If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html

Details:

/home/konrad/code/flip/flip-notification/app/node_modules/serialize-error/index.js:1
export class NonError extends Error {
^^^^^^

SyntaxError: Unexpected token 'export'
    at compileFunction (<anonymous>)

`

CommonJS

Hi Sindre,

could you please provide a "CommonJS" version as well?

Best regards,
Hans

Enumerable properties are made non-enumerable

Enumerable properties are made non-enumerable after deserialization.

import { deserializeError, serializeError } from 'serialize-error';

class MyError extends Error {
  constructor(public params: any) {
    super('test');
  }
}

const error = new MyError({ name: 'foo' });
const serialized = serializeError(error);
const deserialized = deserializeError(serialized) as MyError;

// properties of deserialized.params are non-enumerable

Minimal reproduction: https://stackblitz.com/edit/typescript-dkbp39?file=index.ts

Merge stack traces

Tangentially related to this module, but is there any easy way to join the current stack trace (e.g. Error.captureStackTrace) with the the serialized error’s? Throwing a pre-existing error as-is may be confusing because its stack trace does not contain the current throw line’s stack.

Is the modern way to handle this via .cause?

const current = new Error('Got an error')
current.cause = deserializeError(remoteError);
throw current;

Unfortunately I think it's not going to be well-supported for quite a while.

OutOfMemory exception on errors containing Buffers

I use serialize-error as a way to handle all generic errors coming from different tiers service. It works well most of the time, except for Slack.

I use Slack SDK (https://github.com/SlackAPI/node-slack-sdk) to send, well, messages in Slack.
When I try to send a message to an inexistent Channel, an exception is thrown, with a pretty big Error.
It contains an object that is a Buffer. When serialize-error try to stringify it, I got an error that make the whole server crash.

Is there a way to have a better handling of Buffer ?

The logs:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

1: 0x8dc1c0 node::Abort() [/usr/local/bin/node]
 2: 0x8dc20c  [/usr/local/bin/node]
 3: 0xad60ae v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
 4: 0xad62e4 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node]
 5: 0xec3972  [/usr/local/bin/node]
 6: 0xec3a78 v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [/usr/local/bin/node]
 7: 0xecfb52 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/usr/local/bin/node]
 8: 0xed0484 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node]
 9: 0xed30f1 v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/usr/local/bin/node]
10: 0xe9b3d5  [/usr/local/bin/node]
11: 0xea2c4a v8::internal::Factory::NewRawOneByteString(int, v8::internal::PretenureFlag) [/usr/local/bin/node]
12: 0x11d2845 v8::internal::IncrementalStringBuilder::Extend() [/usr/local/bin/node]
13: 0xf9ce88 v8::internal::JsonStringifier::SerializeArrayLikeSlow(v8::internal::Handle<v8::internal::JSReceiver>, unsigned int, unsigned int) [/usr/local/bin/node]
14: 0xf993f6 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
15: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
16: 0xf9a1e5 v8::internal::JsonStringifier::SerializeJSReceiverSlow(v8::internal::Handle<v8::internal::JSReceiver>) [/usr/local/bin/node]
17: 0xf98e41 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
18: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
19: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
20: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
21: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
22: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
23: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
24: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
25: 0xf9a1e5 v8::internal::JsonStringifier::SerializeJSReceiverSlow(v8::internal::Handle<v8::internal::JSReceiver>) [/usr/local/bin/node]
26: 0xf98e41 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
27: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
28: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
29: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
30: 0xf9c6fd v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<false>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
31: 0xf9cd4e v8::internal::JsonStringifier::SerializeArrayLikeSlow(v8::internal::Handle<v8::internal::JSReceiver>, unsigned int, unsigned int) [/usr/local/bin/node]
32: 0xf993f6 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
33: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
34: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
35: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
36: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
37: 0xf991e1 v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<true>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
38: 0xf9c6fd v8::internal::JsonStringifier::Result v8::internal::JsonStringifier::Serialize_<false>(v8::internal::Handle<v8::internal::Object>, bool, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
39: 0xf9d459 v8::internal::JsonStringifier::Stringify(v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) [/usr/local/bin/node]
40: 0xba4eb1 v8::internal::Builtin_JsonStringify(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/local/bin/node]
41: 0x31ef91bdbf7d

Errors with error properties are serialized incorrectly

If you pass an error to serialize-errors, and one of its properties is itself an error, that error will be missing name/stack/message properties. Errors in my team's project use an "innerError" property, similar to Exception.InnerException in C#, and this causes us to lose useful debugging information.

Nested Error's are not deserialized

When I use nested Error's they are serialized, but not deserialized, am I missing something?:

const {serializeError, deserializeError} = require('serialize-error');

class MyError extends Error {    
    constructor(message, innerError) {
        super(message);

        this.innerError = innerError;
    }
}

const error = new MyError('Error message', new Error('inner error'));
const serialized = serializeError(error)
console.log(serialized);
/* outputs:
{
    name: 'Error',
    message: 'Error message',
    stack: '<cut>',
    innerError: {
        name: 'Error',
        message: 'inner error',
        stack: '<cut>'
    }
}
*/
const deserialized = deserializeError(serialized);
console.log(deserialized);
/* outputs:
Error: Error message
    at <cut> {
  innerError: {}
}
*/

Can type-fest be moved to devDependencies?

As far as I can tell, type-fest only provides TypeScript types (as opposed to values) which are entirely erased at runtime. Thus it would seem we only need type-fest available during compilation, which would be satisfied if it were a dev dependency. Am I missing something?

Version 9.1.1 with CRA default Babel config gives "Module parse failed"

Hi all,

after updating the library to the version 9.1.1 when i start debugging my React App (CRA) (npm start -> react-scripts start) gives the following error:

Failed to compile.

./node_modules/serialize-error/index.js 2:7
Module parse failed: Unexpected token (2:7)
File was processed with these loaders:
 * ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| export class NonError extends Error {
>   name = 'NonError';
| 
|   constructor(message) {

The updated serialize-error is a dependency of a private node-module.
The babel config is the default one, the app is not ejected. No custom webpack config.

Reverting to 9.1.0 resolves the problem.

PS

After checking the between releases differences, i think the problem has been generated from this commit: 88a95be

Feat: Add a option to indicate how deep to scan Error object

Issuehunt badges

Would be great to add an optional argument to indicate how deep to scan Error object.
For example Axios Error object keeps a lot of information inside (object in object in object...).
Would be great to indicate how deep to scan Error object, because sometimes, the output it's too big.
Sorry for my bad english.
Great library, thank you.


IssueHunt Summary

ltburruel ltburruel has been rewarded.

Backers (Total: $40.00)

Submitted pull Requests


Tips

Keep old versions of the lib on a separate branch

Some big and legacy systems can't upgrade to new versions of nodejs. The life of developers that maintain this systems will be more easy if the code of old versions of this lib are preserved in a branch. Optionally adding the nodejs version constraints at package.json#engines.node

Bug: Does not work correctly with Node 6

When serializing Error that is defined like this:

  const error: any = new Error('Something broke down')
  error.details = {
    timestamp: '2019-11-6'
  }

the end result is {}

Add option to decide behavior

You can throw anything in JS, but it's a bad practice. I'd like to have it fail rather than silently accept anything. Though, it's not a good default, so should be opt-in.

Actually, there's 3 possible behaviors:

  1. Always return an object. (wanted default)
  2. Passthrough non-object values. (current default)
  3. Throw on non-object input.

I'd like 1 to be the default behavior, for consistency, but add an option to be able to opt into 2 (which is the current behavior) or 3 (which would be the strict behavior). This should be a single option.

Another benefit of 1 is that the TypeScript types can be better. We can guarantee it's an object and we can also guarantee that the name, message, and stack properties exist.

Error [ERR_REQUIRE_ESM]: require() of ES

Error [ERR_REQUIRE_ESM]: require() of ES Module /var/runtime/app/node_modules/serialize-error/index.js from /var/runtime/app/src/webhooks/sendgrid.ts not supported.
Instead change the require of index.js in [redacted] to a dynamic import() which is available in all CommonJS modules.

import { serializeError } from 'serialize-error';

tsconfig:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "module": "commonjs",
    "outDir": "dist",
    "skipLibCheck": true,
    "strict": true,
    "target": "ES2020",
    "lib": ["esnext"]
  },
  "exclude": [
    "infrastructure/**/*.ts",
    "test/**/*.ts"
  ]
}

`toJSON` example is confusing

The readme example for toJSON doesn't make sense:

import {serializeError} from https://github.com/sindresorhus/serialize-error;

class ErrorWithToJSON extends Error {
	constructor() {
		super('πŸ¦„');
		this.date = new Date();
	}

	toJSON() {
		return serializeError(this);
	}
}

const error = new ErrorWithToJSON();

console.log(serializeError(error));
// => {date: '1970-01-01T00:00:00.000Z', message: 'πŸ¦„', name, stack}

https://github.com/sindresorhus/serialize-error#serializeerrorvalue-options

It seems to suggest creating a toJSON method on custom errors to automatically serialize them correctly (which isn't the intention of the demo), but then calls serializeError on the error itself (when in reality JSON.stringify(error) now already works due to the toJSON property)

I think it should be updated to only be set on a property of the error, or else it's a bit confusing. e.g.

const error = new Error('X')
error.data = {toJSON() {return 'x'}}
serializeError(error)

Error require of ES Module not supported

Since 9.x (I also tried 10.x and 11.x) when importing serialize-error:

import { serializeError } from 'serialize-error';

I get the following error:

Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/mick/git/myproject/node_modules/serialize-error/index.js from /Users/mick/myproject/src/myfile.ts not supported.
Instead change the require of index.js in /Users/myproject/myfile.ts to a dynamic import() which is available in all CommonJS modules.

It works when building with tsc but not when starting with ts-node. Any idea what this could be? I dont find any require() somewhere...

Wrong typescript declaration for errorConstructors

Argument of type '(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[][]' is not assignable to parameter of type 'Iterable<readonly [string, ErrorConstructor]>'.
      The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types.
        Type 'IteratorResult<(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[], any>' is not assignable to type 'IteratorResult<readonly [string, ErrorConstructor], any>'.
          Type 'IteratorYieldResult<(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[]>' is not assignable to type 'IteratorResult<readonly [string, ErrorConstructor], any>'.
            Type 'IteratorYieldResult<(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[]>' is not assignable to type 'IteratorYieldResult<readonly [string, ErrorConstructor]>'.
              Type '(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[]' is not assignable to type 'readonly [string, ErrorConstructor]'.
                Target requires 2 element(s) but source may have fewer.
  Overload 2 of 4, '(entries?: readonly (readonly [string, ErrorConstructor])[] | null | undefined): Map<string, ErrorConstructor>', gave the following error.
    Argument of type '(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[][]' is not assignable to parameter of type 'readonly (readonly [string, ErrorConstructor])[]'.
      Type '(string | SystemError | AssertionError | EvalErrorConstructor | { new (message?: string | undefined, name?: string | undefined): DOMException; ... 25 more ...; readonly WRONG_DOCUMENT_ERR: number; })[]' is not assignable to type 'readonly [string, ErrorConstructor]'.

This message is shown when the code is converted to Typescript.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.