Repository navigation
Description
Version
v26.11.1. Also reproduced on v26.9.0; v26.2.0 behaves differently.
Platform
Windows x64 (`win32`, `x64`). Other platforms have not been tested.
Subsystem
node:vm
What steps will reproduce the bug?
Run the following CommonJS script with node repro.cjs. No third-party dependencies or special flags are needed.
const vm = require('node:vm');
const code = `
let getterCalls = 0;
Object.defineProperty(globalThis, 'throwingProperty', {
configurable: true,
get() {
getterCalls++;
throw new Error('getter failure');
}
});
let outcome;
try {
outcome = { caught: false, value: String(globalThis.throwingProperty) };
} catch (error) {
outcome = { caught: true, message: error.message };
}
({ ...outcome, getterCalls });
`;
console.log(JSON.stringify({ node: process.version, v8: process.versions.v8, platform: process.platform, arch: process.arch }));
for (const [mode, context] of [
['contextified', {}],
['ordinary', vm.constants.DONT_CONTEXTIFY],
]) {
console.log(mode, JSON.stringify(vm.runInNewContext(code, context)));
}How often does it reproduce? Is there a required condition?
The example produced the same output in five runs on each tested version. The getter is defined inside the VM context, directly on globalThis.
What is the expected behavior? Why is that the expected behavior?
Reading the property should propagate the error thrown by its getter to the surrounding catch. The result should contain caught: true and message: "getter failure", rather than treating the read as successful and returning undefined.
This is what happens with vm.constants.DONT_CONTEXTIFY on all three tested versions. The default context also catches the exception on v26.2.0.
What do you see instead?
On v26.9.0 and v26.11.1, the behavior output is:
contextified {"caught":false,"value":"undefined","getterCalls":1}
ordinary {"caught":true,"message":"getter failure","getterCalls":1}
The script uses String() to make an undefined result visible in the JSON output.
| Node | V8 | Default context catches exception | Getter calls in default context |
|---|---|---|---|
| v26.2.0 | 14.6.202.34-node.20 | Yes | 3 |
| v26.9.0 | 14.6.202.34-node.32 | No | 1 |
| v26.11.1 | 14.6.202.34-node.37 | No | 1 |
With DONT_CONTEXTIFY, the exception is caught and the getter runs once on all three versions.
Additional information
#63549 may be related, but no bisect has been performed and the first affected release has not been identified. This report concerns explicit getter reads, rather than the Proxy property-query issue addressed by #63742.
Activity
bunnysayzz commentedon Oct 8, 2026
Root cause is in
ContextifyContext::PropertyGetterCallback(src/node_contextify.cc:557). When a getter defined on the sandbox throws, bothGetRealNamedPropertycalls come back empty, and the function falls through toreturn Intercepted::kNoat :592 without rethrowing. Thetry_catch.ReThrow()at :583 only runs in the success branch, so theTryCatchScopedestructor swallows the exception and V8 returnsundefinedinstead of propagating the throw.Fix: rethrow before the
kNoreturn whentry_catch.HasCaught(). I can open a PR with that plus a regression test if a maintainer wants it.naugtur commentedon Oct 8, 2026
LavaMoat tests are also failing on this. (LavaMoat itself works, but is impossible to test with the existing harness)
wwee154 commentedon Oct 29, 2026
Hi, just a quick follow-up on this issue. Is there any update on the review or planned resolution for the proposed fix in PR #66609? Thanks for all your work on this.