Repository navigation
Error.stackTraceLimit becomes undefined when running from snapshot #55100
Description
Activity
We are currently manually deleting Error.stackTraceLimit before snapshot serialization in here
node/lib/internal/main/mksnapshot.js
Lines 149 to 159 in f29d2b7
| addSerializeCallback(() => { | |
| stackTraceLimitDesc = ObjectGetOwnPropertyDescriptor(Error, 'stackTraceLimit'); | |
| if (stackTraceLimitDesc !== undefined) { | |
| // We want to use null-prototype objects to not rely on globally mutable | |
| // %Object.prototype%. | |
| ObjectSetPrototypeOf(stackTraceLimitDesc, null); | |
| process._rawDebug('Deleting Error.stackTraceLimit from the snapshot. ' + | |
| 'It will be re-installed after deserialization'); | |
| delete Error.stackTraceLimit; | |
| } |
Some background on this behavior: when V8 deserialize the snapshot, it will try to re-install Error.stackTraceLimit (which can vary depending on --stack-trace-limit), so we are left with two options:
- During deserialization, don't try to re-install the limit if it's already in the snapshot, and just keep whatever that's in the snapshot, which I tried to upstream in https://chromium-review.googlesource.com/c/v8/v8/+/3319481
- During serialization, remove whatever limit that's configured in the snapshot builder script, so that V8 can re-install it during deserialization - this was suggested by the V8 team in the aforementioned CL, and was what I implemented in bootstrap: fixup Error.stackTraceLimit for user-land snapshot #44203
It does seems strange though why the limit isn't re-initialized to 10 (the default of --stack-trace-limit) after snapshot deserialization, I'll debug a bit to find out.
So V8 doesn't actually install Error.stackTraceLimit when the context is created for building a snapshot (in addition V8 doesn't install several APIs, most importantly WebAssembly, when it's in a context created for snapshot building). The serialization callback we have just tries to restore the user-mutated context back to the shape that the V8 context deserializer expects. I think we have a few options:
- Try to convince V8 to add a mode to initialize
Error.stackTraceLimitand/orWebAssemblyetc. for contexts created for snapshot building, so that the builder scripts can use them - Initialize
Error.stackTraceLimitfrom--stack-trace-limitourselves during snapshot building (I don't think we can do much aboutWebAssembly, unless we want to re-implement it using the V8 API, which doesn't seem very feasible, and also I suspect V8 just doesn't support snapshotting compiled WebAssembly anyway) - Just documents that
Error.stackTraceLimitandWebAssemblyare not available in snapshot builder scripts. Users can fixError.stackTraceLimitup themselves if needed before using the limit, but it will be re-initialized by V8 using--stack-trace-limitvalue upon deserialization, unless users have another deserialization callback to override it after the V8 initialization.
3 would be the easiest, I am skeptical whether 1 would actually be accepted by the upstream.
2 (specifically the stackTraceLimit, not webassembly) sounds good to me. If there aren't security concerns about having stack traces enabled by default during the snapshot building. This feels user friendly for getting started with snapshots.
3 also sounds good if 2 isn't viable.
Version
v22.8.0
Platform
Subsystem
No response
What steps will reproduce the bug?
Description
Error.stackTraceLimitisundefinedwhile the snapshot is being run, meaning code which "resets"stackTraceLimitafter temporarily modifying it is consequently setting it toundefinedwhich is persisted when resuming from the snapshot.I hit this when importing
typescriptwhile building the snapshot.Setup
Create this file:
No snapshots:
> node app.js warmup: Error.stackTraceLimit is 10 task: typeof err.stack is stringWith snapshots:
How often does it reproduce? Is there a required condition?
100%
What is the expected behavior? Why is that the expected behavior?
I was expecting
Error.stackTraceLimitto have the same starting value regardless of if--build-snapshotis passed.As a result I was also expecting
error.stackwithintaskto be defined when running from the snapshot, because it is defined when running without the snapshot.What do you see instead?
Error.stackTraceLimitisundefinedwhen running with--build-snapshot.As a result
typeof error.stackisstringwhen running the code normally andundefinedwhen using a snapshot.Additional information
Startup snapshots parent issue: #44014
error.stackbeingundefinedwhile building the snapshot - I presume this is there as a security precaution to avoid leaking implementation details of the file system into the snapshot? Maybe this could be documented? (apologies if this is already the case)I wonder if the logic for restoring
Error.stackTraceLimitshould only happen if the snapshot builder explicitly setsError.stackTraceLimitto a number and not toundefined- becauseundefinedis the starting state it hasn't actually been changed by the snapshot builder.The workaround I have in place is to explicitly set
Error.stackTraceLimit = 10;before exiting the snapshot builder. This works, however it would be nice to not hard-code the size and instead allow it to retain the default. EDIT:delete Error.stackTraceLimit;at the end of the snapshot building seems to be a 'better' workaround.