[GH-ISSUE #3473] [Bug] electronActiveHook test fails when ancestor directory contains an electron package due to CJS resolution precedence over NODE_PATH #1298

Closed
opened 2026-10-16 21:03:54 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @keeponlight on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3473

Originally assigned to: @N0zoM1z0 on GitHub.

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

Running tests/boundary/browser/electronActiveHook.test.ts on a system where an ancestor directory above the repository checkout (such as $HOME/node_modules/electron) contains the npm electron package causes 2 out of 3 tests to fail:

  1. retains active hook evidence within its event-retention budget: crashes with an unhandled exception in the spawned child process:
    AssertionError: .../scripts/electron-active-hook.cjs:630
        throw cause;
        ^
    TypeError: window.webContents.windowOpenHandler is not a function
        at .../exercise.cjs:16:49
    : expected 7 to be +0
    
  2. classifies a dropped browser preload as preload configuration at capture: fails with:
    AssertionError: expected [] to deep equally contain { count: 1, family: "preload-configuration" }
    

The test runner spawns node -r <repo>/scripts/electron-active-hook.cjs <tmpdir>/exercise.cjs with env.NODE_PATH=<tmpdir> pointing to a temporary directory containing mock electron.js.

Under Node.js CommonJS resolution (Module._resolveLookupPaths), Node traverses ancestor node_modules directories from the location of the requiring file (<repo>/scripts/electron-active-hook.cjs) before consulting NODE_PATH. If any ancestor directory above the checkout (e.g. $HOME/node_modules/electron) contains the npm electron package (which exports a string path to the executable), scripts/electron-active-hook.cjs resolves that ancestor package ($HOME/node_modules/electron/index.js) instead of <tmpdir>/electron.js.

Because electron.app and electron.ipcMain on the string export are undefined, electron-active-hook.cjs silently fails to patch the mock Electron APIs. Meanwhile, the child script <tmpdir>/exercise.cjs (located in <tmpdir>) has no ancestor electron package, falls back to NODE_PATH, and loads the unpatched <tmpdir>/electron.js. Since BrowserWindow creation was never hooked by electron-active-hook.cjs, window.webContents.windowOpenHandler is never defined, causing exercise.cjs to crash on invocation.

Reproduction

Executed on revision cbc0e97 (Node v24.14.1, macOS Darwin 24.6.0 arm64):

  1. Ensure an ancestor directory above the repository checkout contains the npm electron package (for example, in $HOME):
    mkdir -p ~/node_modules/electron
    echo 'module.exports = "/Applications/Electron.app/Contents/MacOS/Electron";' > ~/node_modules/electron/index.js
    echo '{"name":"electron","version":"42.5.2","main":"index.js"}' > ~/node_modules/electron/package.json
    
  2. Run the boundary test:
    npx vitest run tests/boundary/browser/electronActiveHook.test.ts
    
  3. Observe test failure:
    FAIL tests/boundary/browser/electronActiveHook.test.ts > retains active hook evidence within its event-retention budget
    AssertionError: .../scripts/electron-active-hook.cjs:630
        throw cause;
        ^
    TypeError: window.webContents.windowOpenHandler is not a function
    : expected 7 to be +0
    

Expected behavior

The test harness should isolate module resolution for both -r <repo>/scripts/electron-active-hook.cjs and the exercise script so that they resolve the intended mock module without being affected by electron packages present in ancestor directories ($HOME/node_modules, parent workspace directories, etc.).

Impact and scope

Any developer or CI environment with an electron package installed in a parent workspace directory, user home directory (~/node_modules), or global search path encounters broken boundary tests with exit code 7. The failure is an environment isolation defect rather than a functional defect in active capture itself.

REA version

Source cbc0e97 (main)

Runtime and platform

Node 24.14.1; macOS Darwin 24.6.0 arm64

Originally created by @keeponlight on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3473 Originally assigned to: @N0zoM1z0 on GitHub. ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Process capture or runtime observation ### Observed behavior Running `tests/boundary/browser/electronActiveHook.test.ts` on a system where an ancestor directory above the repository checkout (such as `$HOME/node_modules/electron`) contains the npm `electron` package causes 2 out of 3 tests to fail: 1. `retains active hook evidence within its event-retention budget`: crashes with an unhandled exception in the spawned child process: ``` AssertionError: .../scripts/electron-active-hook.cjs:630 throw cause; ^ TypeError: window.webContents.windowOpenHandler is not a function at .../exercise.cjs:16:49 : expected 7 to be +0 ``` 2. `classifies a dropped browser preload as preload configuration at capture`: fails with: ``` AssertionError: expected [] to deep equally contain { count: 1, family: "preload-configuration" } ``` The test runner spawns `node -r <repo>/scripts/electron-active-hook.cjs <tmpdir>/exercise.cjs` with `env.NODE_PATH=<tmpdir>` pointing to a temporary directory containing mock `electron.js`. Under Node.js CommonJS resolution (`Module._resolveLookupPaths`), Node traverses ancestor `node_modules` directories from the location of the requiring file (`<repo>/scripts/electron-active-hook.cjs`) before consulting `NODE_PATH`. If any ancestor directory above the checkout (e.g. `$HOME/node_modules/electron`) contains the npm `electron` package (which exports a string path to the executable), `scripts/electron-active-hook.cjs` resolves that ancestor package (`$HOME/node_modules/electron/index.js`) instead of `<tmpdir>/electron.js`. Because `electron.app` and `electron.ipcMain` on the string export are `undefined`, `electron-active-hook.cjs` silently fails to patch the mock Electron APIs. Meanwhile, the child script `<tmpdir>/exercise.cjs` (located in `<tmpdir>`) has no ancestor `electron` package, falls back to `NODE_PATH`, and loads the unpatched `<tmpdir>/electron.js`. Since `BrowserWindow` creation was never hooked by `electron-active-hook.cjs`, `window.webContents.windowOpenHandler` is never defined, causing `exercise.cjs` to crash on invocation. ### Reproduction Executed on revision `cbc0e97` (Node v24.14.1, macOS Darwin 24.6.0 arm64): 1. Ensure an ancestor directory above the repository checkout contains the npm `electron` package (for example, in `$HOME`): ```bash mkdir -p ~/node_modules/electron echo 'module.exports = "/Applications/Electron.app/Contents/MacOS/Electron";' > ~/node_modules/electron/index.js echo '{"name":"electron","version":"42.5.2","main":"index.js"}' > ~/node_modules/electron/package.json ``` 2. Run the boundary test: ```bash npx vitest run tests/boundary/browser/electronActiveHook.test.ts ``` 3. Observe test failure: ``` FAIL tests/boundary/browser/electronActiveHook.test.ts > retains active hook evidence within its event-retention budget AssertionError: .../scripts/electron-active-hook.cjs:630 throw cause; ^ TypeError: window.webContents.windowOpenHandler is not a function : expected 7 to be +0 ``` ### Expected behavior The test harness should isolate module resolution for both `-r <repo>/scripts/electron-active-hook.cjs` and the exercise script so that they resolve the intended mock module without being affected by `electron` packages present in ancestor directories (`$HOME/node_modules`, parent workspace directories, etc.). ### Impact and scope Any developer or CI environment with an `electron` package installed in a parent workspace directory, user home directory (`~/node_modules`), or global search path encounters broken boundary tests with exit code 7. The failure is an environment isolation defect rather than a functional defect in active capture itself. ### REA version Source `cbc0e97` (main) ### Runtime and platform Node 24.14.1; macOS Darwin 24.6.0 arm64
Author
Owner

@N0zoM1z0 commented on GitHub (Oct 14, 2026):

Confirmed again on main eb44921c4c3682e33c78508285629926e8ef241e, now also on Linux x64 / Node 24.18.0 / npm 11.16.0. This is not restricted to the originally reported macOS host.

Used a separate detached checkout under a private temporary parent; the ordinary user/home/workspace node_modules were not changed. The parent package was a controlled npm-Electron-compatible string export (module.exports = "/controlled/unused/electron"); no real Electron executable was launched.

Executed the actual tests/boundary/browser/electronActiveHook.test.ts in an A/B/A control:

Parent resolver state Passed Failed
No ancestor electron package 3 0
Parent node_modules/electron exports the executable path 1 2
Controlled parent package removed 3 0

The two failing cases are the same as reported: window.webContents.windowOpenHandler is not a function (child exit 7) and absent preload-configuration evidence. The fatal-child case remains passing. Checked JSON failure messages, not only the aggregate exit code.

The hook/test and workflow fixture sources are unchanged in subsequent main 1438422dfedf1de29368b595e09a04f1fc10750d. No production change, global resolution patch or timeout adjustment was made during this audit. Fix belongs at the test module-resolution owner so both preloaded hook and exercise resolve the same controlled module; installed Electron/native runtime behavior is not established by these fixture controls.

<!-- gh-comment-id:6172113533 --> @N0zoM1z0 commented on GitHub (Oct 14, 2026): Confirmed again on main `eb44921c4c3682e33c78508285629926e8ef241e`, now also on **Linux x64 / Node 24.18.0 / npm 11.16.0**. This is not restricted to the originally reported macOS host. Used a separate detached checkout under a private temporary parent; the ordinary user/home/workspace node_modules were not changed. The parent package was a controlled npm-Electron-compatible string export (`module.exports = "/controlled/unused/electron"`); no real Electron executable was launched. Executed the actual `tests/boundary/browser/electronActiveHook.test.ts` in an A/B/A control: | Parent resolver state | Passed | Failed | | --- | ---: | ---: | | No ancestor electron package | 3 | 0 | | Parent node_modules/electron exports the executable path | 1 | 2 | | Controlled parent package removed | 3 | 0 | The two failing cases are the same as reported: `window.webContents.windowOpenHandler is not a function` (child exit 7) and absent `preload-configuration` evidence. The fatal-child case remains passing. Checked JSON failure messages, not only the aggregate exit code. The hook/test and workflow fixture sources are unchanged in subsequent main `1438422dfedf1de29368b595e09a04f1fc10750d`. No production change, global resolution patch or timeout adjustment was made during this audit. Fix belongs at the test module-resolution owner so both preloaded hook and exercise resolve the same controlled module; installed Electron/native runtime behavior is not established by these fixture controls.
Author
Owner

@N0zoM1z0 commented on GitHub (Oct 14, 2026):

Merged via #3565 as 1579b6865e8ee87873036a39af3e257cc70d6f21 using normal GitHub enforcement. The actual squash tree equals the validated PR-head tree (9376c6ae1f33a3e808d99e01fa506156a7352c4b), with both requested noreply co-author trailers.

The real Node/CJS clean/ancestor/restored controls now all pass 4/4; bypassing staging makes the new regression fail for the expected missing window-open handler. Current-head CI run 38599199649 succeeded: 11 selected jobs passed, 19 unselected skipped; downloaded report has 1,285 unique files, 14,676 passed, 125 skipped, zero failures or duplicate files. Greptile reviewed the exact head at 5/5 without inline findings. Actual Electron-engine behavior was outside this fixture-isolation correction's scope.

<!-- gh-comment-id:6173467626 --> @N0zoM1z0 commented on GitHub (Oct 14, 2026): Merged via #3565 as `1579b6865e8ee87873036a39af3e257cc70d6f21` using normal GitHub enforcement. The actual squash tree equals the validated PR-head tree (`9376c6ae1f33a3e808d99e01fa506156a7352c4b`), with both requested noreply co-author trailers. The real Node/CJS clean/ancestor/restored controls now all pass 4/4; bypassing staging makes the new regression fail for the expected missing window-open handler. Current-head CI run 38599199649 succeeded: 11 selected jobs passed, 19 unselected skipped; downloaded report has 1,285 unique files, 14,676 passed, 125 skipped, zero failures or duplicate files. Greptile reviewed the exact head at 5/5 without inline findings. Actual Electron-engine behavior was outside this fixture-isolation correction's scope.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1298
No description provided.