Skip to content
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

errWithCause only serializes Error objects #158

Open
darcyrush opened this issue Nov 12, 2024 · 8 comments
Open

errWithCause only serializes Error objects #158

darcyrush opened this issue Nov 12, 2024 · 8 comments

Comments

@darcyrush
Copy link

Javascript can throw anything, not just Error objects. This includes primitives like strings and numbers;

try {
  throw 42; // completely valid
} catch (e) {
  throw new Error("Nested error", { cause: e });
}

The bundled lib.es2022.error.d.ts TypeScript typings indirectly hints at this;

interface Error {
    cause?: unknown;
}

As such, I believe this is unexpected;

const serialize = require("pino-std-serializers").errWithCause;
const serialized = serialize(new Error("Example", { cause: 42 }));
console.log(serialized.cause); // undefined

I would expect the cause to be populated with 42. Likewise I would expect it to be populated if a string was thrown, or whatever else is legal to be thrown in JavaScript, including objects that don't extend the JS Error class. Instead, the cause is simply lost.

This example may seem like a contrived edge case, but I have come across libraries that throw their own "Error" objects, that do not extend from the native JS Error class,

@jsumners
Copy link
Member

Javascript can throw anything, not just Error objects. This includes primitives like strings and numbers;

That is true, but you should NOT do that. You should only throw instances of things that derive from Error.prototype. This library is designed with this principle in mind.

@darcyrush
Copy link
Author

That is true, but you should NOT do that. You should only throw instances of things that derive from Error.prototype.

I completely agree, but best practices do not always translate to reality.

While we can enforce this in our own code, we have no way to enforce this in external npm libraries (or even in very old nodejs core code, potentially). Wrapping every single npm library in a try catch is not simply not feasible. The majority of errors that our projects deal with are thrown from npm libraries.

Given that JS allows this - regardless of how insane it is - i think pino serialization should account for this also.

@jsumners
Copy link
Member

Would you like to send a Pull Request to address this issue? Remember to add unit tests.

darcyrush pushed a commit to darcyrush/pino-std-serializers that referenced this issue Nov 12, 2024
@darcyrush
Copy link
Author

Here is a PR with some questions as comments

@ifeltsweet
Copy link

We also need this because we do something like this:

throw new Error("Request failed", { cause:  { request } }); 

Which is completely valid and useful.

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause

@darcyrush
Copy link
Author

darcyrush commented Nov 28, 2024

We also need this because we do something like this:

throw new Error("Request failed", { cause:  { request } }); 

This is our use case too.

darcyrush pushed a commit to darcyrush/pino-std-serializers that referenced this issue Nov 28, 2024
@voxpelli
Copy link
Contributor

voxpelli commented Nov 28, 2024

@ifeltsweet @darcyrush The preferable way to do that would be to make a RequestError or similar that has a request as a property

Then you can log it in a more desirable way

And strictly speaking the request is probably not the cause of your failure, something in your code is, the request is merely context.

If it's a HTTP code 400 style failure then the cause is that the request didn't pass validation or similar – the cause isn't the request itself.

If it's a HTTP code 500 style failure then the cause is that the request unexpectedly made something error in your code – then the cause is that error, not the request.

Adding the request to the Error may often be redundant as well – your pino setup in your router should annotate your logs with the request context by itself. Eg. Fastify does this when you use req.log and when it catches a thrown error (and a thrown error will always cause a 500 response unless the error is something like a NotFoundError)

@voxpelli
Copy link
Contributor

voxpelli commented Nov 28, 2024

For historical context:

The main reason why Error causes can be any value is because all of throw, Promise.reject(), the catch in a try-catch etc historically can be of any value.

So locking it down to be only Error:s that were allowed would have necessitated people to check whether it's an Error before they add it as a cause.

See original discussion at tc39/proposal-error-cause#21 and also see es-shims/error-cause#2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants