-
Notifications
You must be signed in to change notification settings - Fork 30k
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
worker: serialize errors if stack getter throws #26145
Conversation
The idea behind this line was that I don’t think it would be bad to fall back back to the second case ( |
@addaleax I'm having difficulty finding a way to show this line having an effect. Here's the test I came up with: 'use strict';
const common = require('../common');
const assert = require('assert');
const { Worker } = require('worker_threads');
const w = new Worker(`
const fn = (err) => {
if (err.message === 'fhqwhgads')
throw new Error('come on');
return 'This is my custom stack trace!';
};
Error.prepareStackTrace = fn;
throw new Error('fhqwhgads');
`,
{ eval: true }
);
w.on('message', common.mustNotCall());
w.on('error', common.mustCall((err) => {
console.log(err);
})); But whether I compile with or without the line removed in this PR, the output is the same: Error [ERR_WORKER_UNSERIALIZABLE_ERROR]: Serializing an uncaught exception failed
at Worker.[kOnCouldNotSerializeErr] (internal/worker.js:135:24)
at Worker.[kOnMessage] (internal/worker.js:149:45)
at MessagePort.Worker.(anonymous function).on (internal/worker.js:84:57)
at MessagePort.emit (events.js:197:13)
at MessagePort.onmessage (internal/worker/io.js:62:8) Do I need to revise the test? Or does this show that the line has no effect either way? |
@Trott Yeah, I get the same results. It looks like V8 tries to call diff --git a/lib/internal/error-serdes.js b/lib/internal/error-serdes.js
index 7b4b416b80c6..bba49c782fff 100644
--- a/lib/internal/error-serdes.js
+++ b/lib/internal/error-serdes.js
@@ -36,7 +36,10 @@ function TryGetAllProperties(object, target = object) {
Assign(all, TryGetAllProperties(GetPrototypeOf(object), target));
const keys = GetOwnPropertyNames(object);
ForEach(keys, (key) => {
- const descriptor = GetOwnPropertyDescriptor(object, key);
+ let descriptor;
+ try {
+ descriptor = GetOwnPropertyDescriptor(object, key);
+ } catch { return; }
const getter = descriptor.get;
if (getter && key !== '__proto__') {
try { |
Current code that is intended to handle the stack getter throwing is untested. Add a test and adjust code to function as expected. Co-authored-by: Anna Henningsen <anna@addaleax.net>
be45ab4
to
6e7f287
Compare
OK, added the test, and adjusted the code per @addaleax's comment (and added a CI: https://ci.nodejs.org/job/node-test-pull-request/20899/ (:heavy_check_mark:) |
Thanks! |
@devsnek You probably want to re-review this :) |
let descriptor; | ||
try { | ||
descriptor = GetOwnPropertyDescriptor(object, key); | ||
} catch { return; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am fine with returning undefined but wouldn't it be even better to return the error to the user as well? We could wrap it into something else so it's clear that accessing the property threw (for example by adding a new Node.js error like ERR_THREW_ON_ACCESS
) which contains the actual error as property.
assert.strictEqual(err.stack, undefined); | ||
assert.strictEqual(err.message, 'fhqwhgads'); | ||
assert.strictEqual(err.name, 'Error'); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to change this part to:
w.on('error', common.expectsError({
stack: undefined,
message: 'fhqwhgads',
name: 'Error'
});
i am confused why we wouldn't just let the error bubble up. the entire state of the resulting object could be silently incorrect because of this. |
I'm fine with that. How about you, @addaleax? Error [ERR_WORKER_UNSERIALIZABLE_ERROR]: Serializing an uncaught exception failed
at Worker.[kOnCouldNotSerializeErr] (internal/worker.js:135:24)
at Worker.[kOnMessage] (internal/worker.js:149:45)
at MessagePort.Worker.(anonymous function).on (internal/worker.js:84:57)
at MessagePort.emit (events.js:197:13)
at MessagePort.onmessage (internal/worker/io.js:62:8) |
@Trott @devsnek It’s important to me that some helpful information about the original error survives, and ideally as much as possible. It’s not clear to me how “Serializing an uncaught exception failed” is better than providing an object without a stack trace (which is, admittedly, already pretty unhelpful). |
So that then brings us to @BridgeAR's suggestion of trying to provide some helpful information when it throws. Thinking about it more, I would most likely prefer that we emulate as closely as possible what happens outside of workers so that at least people can debug and not be confused that they get one error outside of workers and a different error in a worker. Here's what happens outside of a worker: /Users/trott/io.js/test/parallel/test-worker-error-stack-getter-throws.js:30
throw new Error('fhqwhgads');
^
Error: fhqwhgads So that would argue for the approach currently here where we return the error with no stack trace at all and no indication of an additional error while creating the stack trace. @devsnek? |
maybe i'm just missing something obvious here, but why isn't this diff just -try { error.stack; } catch {} and nothing else? just let the error bubble up to the serialize() call |
@devsnek That's what it was initially but doing that results in this error in the test: Error [ERR_WORKER_UNSERIALIZABLE_ERROR]: Serializing an uncaught exception failed
at Worker.[kOnCouldNotSerializeErr] (internal/worker.js:135:24)
at Worker.[kOnMessage] (internal/worker.js:149:45)
at MessagePort.Worker.(anonymous function).on (internal/worker.js:84:57)
at MessagePort.emit (events.js:197:13)
at MessagePort.onmessage (internal/worker/io.js:62:8) That's OK, I guess, but it has the following shortcomings:
What's here now may be less-than-ideal, but it has the benefits of:
|
wherever ERR_WORKER_UNSERIALIZABLE_ERROR is thrown we could just re-throw the actual error right? i'd expect try {
postMessage({
get a() {
throw 'xyz';
},
});
} catch (e) {
console.log(e);
} to print 'xyz', just like it does in browser. |
@devsnek Can you clarify what you mean by “re-throw”? This is coming from the uncaught exception handler, not from a try/catch.
That’s already happening here as well. |
ok i see how this code is actually being used now. i would probably just copy what browsers do and ignore the stack property. https://html.spec.whatwg.org/multipage/workers.html#runtime-script-errors-2 |
@devsnek I think the stack trace is too valuable to be omitted. I honestly wouldn’t know what to do if I didn’t have it available when debugging the worker tests. :) |
@devsnek I can't tell if your comments are comments/discussion, or if you're -1 on landing this. Can you let me know? |
@Trott i'm not blocking this |
Landed in 79a3348 |
Current code that is intended to handle the stack getter throwing is untested. Add a test and adjust code to function as expected. Co-authored-by: Anna Henningsen <anna@addaleax.net> PR-URL: nodejs#26145 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Current code that is intended to handle the stack getter throwing is untested. Add a test and adjust code to function as expected. Co-authored-by: Anna Henningsen <anna@addaleax.net> PR-URL: #26145 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Current code that is intended to handle the stack getter throwing is untested. Add a test and adjust code to function as expected. Co-authored-by: Anna Henningsen <anna@addaleax.net> PR-URL: #26145 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Remove an apparently unnecessary try/catch from the errorserialization/deserialization internal module used by worker_threads.
(If I'm wrong about this being unnecessary for some reason, I'll switch course and try to add a test and/or an explanatory comment. But it sure seems to effectively be a no-op?)Current code that is intended to handle the stack getter throwing is untested. Add a test and adjust code to function as expected.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes