You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
V8 Inspector is not available in --require in node --test #48467
Run NODE_OPTIONS="--require ./start-inspect.js" node --test ./hello.test.js
How often does it reproduce? Is there a required condition?
100%
What is the expected behavior? Why is that the expected behavior?
The inspector should open as usual, as if --test was not given in the CLI
What do you see instead?
$ NODE_OPTIONS="--require ./start-inspect.js" node --test ./hello.test.js
node:inspector:181
open(port, host);
^
This Environment was initialized without a V8::Inspector
(Use `node --trace-uncaught ...` to show where the exception was thrown)
Node.js v20.3.0
Additional information
This does not happen if --test is omitted from the command. This causes issues with the VS/Code debugger which uses this mechanism to initiate debugging. I think Jetbrains does the same thing, so Webstorm and friends probably have the same issue, though I have not tested it.
Also, if I try to open the inspector in the test file itself, it works fine, so it seems like there might be some initialization ordering that's different for --test.
I think this is because the code is trying to access the inspector both in the parent (orchestrator) process, and in the child process. I guess we should provide a way to determine whether you are inside a test file or not. @cjihrig WDYT?
Edit:
On second thought, This is expected to fail on the parent process, and can be handled with a simple try/catch.
if you really need to know you are now in the main process you can look at the NODE_TEST_CONTEXT environment variable
I think we may want to look into skipping preloads in the test runner main process, but still propagating the preload flags to the child processes. I can't think of a good reason why preloads need to run for the main process.
This issue has been marked as stale due to 210 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.
This issue has been marked as stale due to 90 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.
I'd like to pick this up, but since #52099 was blocked (semver-major, and it breaks --require-based TypeScript reporters/hooks in the orchestrator, per @GeoffreyBooth's review), I want to agree on an approach before opening a PR.
Root cause. The error doesn't come from preloading itself. Since #44520 (--inspect support for --test), the orchestrator process never creates a V8 inspector, so --inspect* flags only take effect in the child test processes. That is the !(options_->test_runner && options_->test_isolation == "process") term in Environment::should_create_inspector() (src/env-inl.h), and --watch has the same term. --require modules still run in the orchestrator, which is intentional because it's how people register hooks and TS reporters. When one of them calls inspector.open(), which is what the VS Code and JetBrains bootloaders do, Agent::Start() was never called for this environment and the call throws ERR_INSPECTOR_NOT_AVAILABLE ("This Environment was initialized without a V8::Inspector").
Proposal: create the inspector in the orchestrator, but don't activate it from CLI flags.
Remove the test_runner/watch_mode terms from should_create_inspector(), so the orchestrator gets an inspector client the way any plain node process does.
In Environment::InitializeInspector(), when the process is the test orchestrator (or the watch-mode parent), start the agent with a copy of debug_options() that has inspector_enabled, break_first_line, break_node_first_line and inspect_wait cleared. --inspect, --inspect-brk and --inspect-wait keep their current meaning: they are forwarded to the child processes (inspectPort, concurrency 1, etc.), and the parent neither listens nor pauses.
Leave profiler::StartProfilers() (NODE_V8_COVERAGE, --cpu-prof, --heap-prof) and the SIGUSR1 handler disabled in the orchestrator, as they are today. That way coverage tools and the child inspect ports see no change.
After this, inspector.open() from a preload in the orchestrator behaves as it does in a non-inspected node process: it opens a session the IDE can attach to. Child processes are unchanged. Nothing that works today stops working: no preload is skipped, and the flags behave the same. The only observable difference is that a call that used to throw now succeeds, so I think this is semver-minor rather than major.
Questions before I write it:
Is it OK for the orchestrator to become debuggable through an explicit inspector.open()? IDE bootloaders would then attach to the orchestrator as well. connor4312 can probably say whether js-debug handles that well; it seems to already treat every process as a target.
Should --watch get the same treatment? The cause is identical. I'd rather do both in one PR for consistency.
Would you prefer a smaller alternative: keep things as they are, but give the orchestrator a clearer error and document NODE_TEST_CONTEXT as the way for bootloaders to detect it? That fixes nothing for IDEs, so I'd lean towards the proposal above.
I have a prototype of this on a branch: https://github.com/SahinurDEV/node/tree/inspector-test-orchestrator (about 30 lines in src/env-inl.h, src/node.cc and src/inspector_profiler.cc, plus a regression test). If this direction sounds right, I'll open a PR with tests in test/parallel covering --require + inspector.open() with --test (and --watch), --inspect-brk + --test still pausing only in the children, and NODE_V8_COVERAGE output unchanged for the orchestrator.
AI-assistance disclosure: I used an AI coding assistant to help investigate this and draft this comment. I have reviewed the analysis myself.
Activity
MoLow commentedon Jun 25, 2023
I think this is because the code is trying to access the inspector both in the parent (orchestrator) process, and in the child process.
I guess we should provide a way to determine whether you are inside a test file or not. @cjihrig WDYT?Edit:
On second thought, This is expected to fail on the parent process, and can be handled with a simple
try/catch.if you really need to know you are now in the main process you can look at the
NODE_TEST_CONTEXTenvironment variableplease LMK if this helps you.
cjihrig commentedon Jun 25, 2023
I think we may want to look into skipping preloads in the test runner main process, but still propagating the preload flags to the child processes. I can't think of a good reason why preloads need to run for the main process.
rluvaton commentedon Jul 20, 2023
Tested and had the same problem in webstorm as well
damianobarbati commentedon Feb 19, 2024
Any news on this? I also have the debugger broken in webstorm when using tsx :(
github-actions commentedon May 31, 2026
This issue has been marked as stale due to 210 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.
connor4312 commentedon Jun 1, 2026
Not stale
github-actions commentedon Sep 1, 2026
This issue has been marked as stale due to 90 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.
connor4312 commentedon Sep 1, 2026
Not stale
SahinurDEV commentedon Oct 12, 2026
I'd like to pick this up, but since #52099 was blocked (semver-major, and it breaks
--require-based TypeScript reporters/hooks in the orchestrator, per @GeoffreyBooth's review), I want to agree on an approach before opening a PR.Root cause. The error doesn't come from preloading itself. Since #44520 (
--inspectsupport for--test), the orchestrator process never creates a V8 inspector, so--inspect*flags only take effect in the child test processes. That is the!(options_->test_runner && options_->test_isolation == "process")term inEnvironment::should_create_inspector()(src/env-inl.h), and--watchhas the same term.--requiremodules still run in the orchestrator, which is intentional because it's how people register hooks and TS reporters. When one of them callsinspector.open(), which is what the VS Code and JetBrains bootloaders do,Agent::Start()was never called for this environment and the call throwsERR_INSPECTOR_NOT_AVAILABLE("This Environment was initialized without a V8::Inspector").Proposal: create the inspector in the orchestrator, but don't activate it from CLI flags.
test_runner/watch_modeterms fromshould_create_inspector(), so the orchestrator gets an inspector client the way any plainnodeprocess does.Environment::InitializeInspector(), when the process is the test orchestrator (or the watch-mode parent), start the agent with a copy ofdebug_options()that hasinspector_enabled,break_first_line,break_node_first_lineandinspect_waitcleared.--inspect,--inspect-brkand--inspect-waitkeep their current meaning: they are forwarded to the child processes (inspectPort, concurrency 1, etc.), and the parent neither listens nor pauses.profiler::StartProfilers()(NODE_V8_COVERAGE,--cpu-prof,--heap-prof) and the SIGUSR1 handler disabled in the orchestrator, as they are today. That way coverage tools and the child inspect ports see no change.After this,
inspector.open()from a preload in the orchestrator behaves as it does in a non-inspectednodeprocess: it opens a session the IDE can attach to. Child processes are unchanged. Nothing that works today stops working: no preload is skipped, and the flags behave the same. The only observable difference is that a call that used to throw now succeeds, so I think this is semver-minor rather than major.Questions before I write it:
inspector.open()? IDE bootloaders would then attach to the orchestrator as well. connor4312 can probably say whether js-debug handles that well; it seems to already treat every process as a target.--watchget the same treatment? The cause is identical. I'd rather do both in one PR for consistency.NODE_TEST_CONTEXTas the way for bootloaders to detect it? That fixes nothing for IDEs, so I'd lean towards the proposal above.I have a prototype of this on a branch: https://github.com/SahinurDEV/node/tree/inspector-test-orchestrator (about 30 lines in
src/env-inl.h,src/node.ccandsrc/inspector_profiler.cc, plus a regression test). If this direction sounds right, I'll open a PR with tests intest/parallelcovering--require+inspector.open()with--test(and--watch),--inspect-brk+--teststill pausing only in the children, andNODE_V8_COVERAGEoutput unchanged for the orchestrator.AI-assistance disclosure: I used an AI coding assistant to help investigate this and draft this comment. I have reviewed the analysis myself.