sindresorhus / serialize-error Goto Github PK
View Code? Open in Web Editor NEWSerialize/deserialize an error into a plain object
License: MIT License
Serialize/deserialize an error into a plain object
License: MIT License
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.
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 β€οΈ
Getting this error
"TypeError: Cannot read property 'pipe' of null
at destroyCircular (packages/modules.js:1758:49)
at destroyCircular (packages/modules.js:1775:14)
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?
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"
]
}
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?
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:
object
. (wanted default)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.
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:
Error
instead, here:Line 173 in 6f6102f
errorConstructors.set('CustomError', message => new CustomError(message, {}))
const errorToSerialize: unknown = "hello world";
const serialized = serializeError(errorToSerialize);
// serialized <=> ErrorObject
// typeof serialized === "string'
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)
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
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>)
`
What is the difference here, if any?
Thanks for this super useful package!
Any chance to publish a dual package with support for native .mjs
imports?
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;
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.
The package works great but since it exports ES6 and my webpack config doesn't transpile node_modules, it breaks in older browsers.
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.
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:
serializeError
into a separate file that can be imported without the deserializeError
code.deserializeError
into a new module.import
, add sideEffects: false
to the package.json
to enable tree shaking in the module.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.
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
Live Demo: https://runkit.com/embed/w5k9fpq8b44e
const {serializeError} = require("serialize-error")
serializeError({
a: 1,
b: new Date(),
c: {cc: 2}
});
// output: Object {a: 1, b: Object {}, c: Object {cc: 2}}
I understand that the very purpose of this package is to produce a plain object, but it might be useful to convert a Date
object into its representative string or milliseconds.
If an error contains a circular reference (entirely possible with AssertionErrors actual
, and expected
), serialization will fail.
Reference: avajs/ava#187
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
Context:
I think this already kind of supported, but it should probably be documented and tested, especially if #48 is implemented
We are having problems with Readable's being part of the error. It would be great to exclude them similar to Buffers.
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
}
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>)
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: {}
}
*/
Cannot add property Symbol(.toJSON was called), object is not extensible
TypeError: Cannot add property Symbol(.toJSON was called), object is not extensible
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
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?
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.
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
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
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
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?
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!
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
};
}
}
serialize-error
automatically uses toJSON
:
Lines 61 to 63 in 855fe3d
Unfortunately this introduces two unwanted behaviors:
{toJSON() { /*h4xor*/ }}
serializeError(error, {toJSON: false})
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.
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.
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 {}
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;
}
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.
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...
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?
Hi Sindre,
could you please provide a "CommonJS" version as well?
Best regards,
Hans
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.