How to Debug Cypress Code Within the Monorepo: Node, Electron, and Browser Strategies
To debug Cypress code within the monorepo, attach Node Inspector to the CLI/server runtime (port 9229), Chrome DevTools to the Electron main process (port 9222), and Chrome DevTools to the browser's AUT/Driver runtime (port 9223), while leveraging cy.debug() for driver breakpoints and Cypress.automation('remote:debugger:protocol') for low-level CDP inspection.
The cypress-io/cypress repository is organized as a complex, multi-runtime monorepo where code executes across Node.js, Electron, and browser environments. When you need to debug Cypress code within the monorepo, you must target the specific runtime responsible for the behavior—whether that's the CLI automation layer, the desktop application's main process, or the test driver injected into the Application Under Test (AUT). This guide covers the exact attachment points, source file locations, and commands used by the Cypress core team to debug each layer.
Understanding the Three Debugging Runtimes
The Cypress architecture spans three distinct execution contexts, each requiring a different debugging strategy:
- Node.js (CLI and Server): Handles the command-line interface, build scripts, and the
@packages/serverautomation layer. Debug using Node Inspector on port 9229. - Electron (Main Process): Hosts the desktop GUI and GraphQL data context. Debug via Chrome DevTools on port 9222.
- Browser (AUT and Driver): Executes the
@packages/driverpackage within the browser context. Debug via Chrome DevTools on port 9223.
Debugging the Driver with cy.debug()
The @packages/driver package provides the cy.debug() command that pauses test execution and injects a debugger statement directly into the browser's execution context.
In packages/driver/src/cy/commands/debugging.ts, the implementation logs command state and triggers the browser debugger:
// packages/driver/src/cy/commands/debugging.ts
Commands.addQuery('debug', function debug (options = {}) {
Cypress.log({ hidden: options.log === false, snapshot: true, end: true, timeout: 0 })
let hasPaused = false
return (subject) => {
if (!hasPaused) {
hasPaused = true
const previous = this.get('prev')
$utils.log('\n%c------------------------ Debug Info ------------------------',
'font-weight: bold;')
$utils.log('Command Name: ', previous && previous.get('name'))
$utils.log('Command Args: ', previous && previous.get('args'))
$utils.log('Current Subject: ', subject)
debugger // ← execution stops here
}
return subject
}
})
When you call cy.debug() in a test, open Chrome DevTools for the AUT to inspect variables, step through the driver code, and evaluate expressions in the current subject context.
Accessing the Remote Debugger Protocol
For low-level browser inspection not exposed through Cypress commands, use the remote:debugger:protocol automation channel. The server routes these commands in packages/server/lib/browsers/cdp-protocol/cdp_automation.ts:
// packages/server/lib/browsers/cdp-protocol/cdp_automation.ts
case 'remote:debugger:protocol':
return this.sendDebuggerCommandFn(data.command, data.params, data.sessionId)
Invoke this from your test code to execute arbitrary Chrome DevTools Protocol (CDP) commands:
// Retrieve internal browser state via CDP
Cypress.automation('remote:debugger:protocol', {
command: 'Runtime.evaluate',
params: { expression: 'window.location.href' },
}).then((result) => {
console.log('Current URL:', result.result.value)
})
This method is essential when debugging internal browser state or Chrome-specific behaviors during test execution.
Debugging Node.js CLI and Server Processes
Most Cypress logic resides in Node.js packages. To debug the CLI or server code, start the development server with the Node Inspector flag:
# Attach Node Inspector to the development server
node --inspect-brk $(which yarn) dev
This opens port 9229 for debugger attachment. You can then connect VS Code or Chrome DevTools to localhost:9229 and set breakpoints in files such as packages/server/lib/browsers/chrome.ts.
Enable verbose internal logging by setting the DEBUG environment variable:
DEBUG=cypress:server,cypress:driver yarn dev
Attaching to the Electron Main Process
When running yarn dev or yarn start, Cypress launches Electron with the --remote-debugging-port=9222 flag, as implemented in packages/electron/src/open.ts.
To debug the main process:
- Launch Cypress with
yarn dev. - Open Chrome and navigate to
chrome://inspect. - Under Remote Target, click inspect next to the Electron entry.
- Set breakpoints in Electron main-process source files like
packages/electron/src/open.ts.
VS Code Configuration for All Runtimes
Configure .vscode/launch.json to attach to all three runtimes simultaneously:
{
"version": "0.2.0",
"configurations": [
{
"name": "Cypress CLI (Node)",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/yarn",
"args": ["dev"],
"runtimeArgs": ["--inspect-brk=9229"],
"console": "integratedTerminal"
},
{
"name": "Electron Main",
"type": "pwa-chrome",
"request": "attach",
"port": 9222,
"webRoot": "${workspaceFolder}"
},
{
"name": "Browser (AUT)",
"type": "pwa-chrome",
"request": "attach",
"port": 9223,
"webRoot": "${workspaceFolder}"
}
]
}
Common Pitfalls and Solutions
| Issue | Cause | Resolution |
|---|---|---|
| Breakpoints not hit | Missing source maps or process not started with --inspect. |
Ensure node --inspect-brk is used and tsconfig.json includes "sourceMap": true. |
cy.debug() ignored |
Running in headless mode (cypress run). |
Use interactive mode (cypress open) or set config.isInteractive = true. |
| CDP commands fail | Browser CDP endpoint not connected. | Wait for after:browser:launch event before calling Cypress.automation. |
| Excessive log noise | DEBUG=cypress:* includes all packages. |
Scope the variable: DEBUG=cypress:server,cypress:driver. |
Summary
- Use
cy.debug()(implemented inpackages/driver/src/cy/commands/debugging.ts) to pause test execution and inspect the driver state in the browser. - Leverage
Cypress.automation('remote:debugger:protocol', ...)(handled inpackages/server/lib/browsers/cdp-protocol/cdp_automation.ts) to send raw CDP commands for deep browser inspection. - Attach Node Inspector to port 9229 when debugging CLI and server-side code in the monorepo.
- Connect to Electron on port 9222 (set in
packages/electron/src/open.ts) to debug the main process. - Enable source maps in
tsconfig.jsonand avoid headless mode when using interactive debugging features.
Frequently Asked Questions
Why does cy.debug() not pause in headless mode?
The cy.debug() command relies on the browser's debugger statement, which is ignored when Cypress runs in headless mode (cypress run). To use cy.debug(), you must run Cypress in interactive mode using cypress open or explicitly set isInteractive: true in your configuration.
Which port should I use to debug the Cypress server code?
Debug the Node.js server and CLI code on port 9229 by starting the development server with node --inspect-brk $(which yarn) dev. This allows you to attach VS Code or Chrome DevTools to the server-side logic in packages like packages/server/lib/browsers/chrome.ts.
How do I inspect variables inside the Electron main process?
The Electron main process exposes a debugging port on 9222 when started via yarn dev. Navigate to chrome://inspect in Chrome, locate the Electron remote target, and click inspect to access the main process context where the desktop GUI and GraphQL data layer execute.
Can I use the remote debugger protocol to modify browser behavior during tests?
Yes. By calling Cypress.automation('remote:debugger:protocol', {command, params}), you can send arbitrary Chrome DevTools Protocol commands to the browser. This allows you to modify network conditions, override user agents, or inspect internal browser state that is not exposed through standard Cypress APIs, as routed through packages/server/lib/browsers/cdp-protocol/cdp_automation.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →