# How to Debug Cypress Code Within the Monorepo: Node, Electron, and Browser Strategies

> Debug Cypress code in your monorepo using Node Inspector, Chrome DevTools, and cy debug. Learn strategies for Node, Electron, and browser runtimes to find and fix bugs faster.

- Repository: [Cypress.io/cypress](https://github.com/cypress-io/cypress)
- Tags: how-to-guide
- Published: 2026-08-06

---

**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/server` automation 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/driver` package 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`](https://github.com/cypress-io/cypress/blob/main/packages/driver/src/cy/commands/debugging.ts), the implementation logs command state and triggers the browser debugger:

```typescript
// 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`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/cdp-protocol/cdp_automation.ts):

```typescript
// 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:

```javascript
// 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:

```bash

# 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`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/chrome.ts).

Enable verbose internal logging by setting the `DEBUG` environment variable:

```bash
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`](https://github.com/cypress-io/cypress/blob/main/packages/electron/src/open.ts).

To debug the main process:

1. Launch Cypress with `yarn dev`.
2. Open Chrome and navigate to `chrome://inspect`.
3. Under **Remote Target**, click **inspect** next to the Electron entry.
4. Set breakpoints in Electron main-process source files like [`packages/electron/src/open.ts`](https://github.com/cypress-io/cypress/blob/main/packages/electron/src/open.ts).

## VS Code Configuration for All Runtimes

Configure [`.vscode/launch.json`](https://github.com/cypress-io/cypress/blob/main/.vscode/launch.json) to attach to all three runtimes simultaneously:

```json
{
  "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`](https://github.com/cypress-io/cypress/blob/main/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 in [`packages/driver/src/cy/commands/debugging.ts`](https://github.com/cypress-io/cypress/blob/main/packages/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 in [`packages/server/lib/browsers/cdp-protocol/cdp_automation.ts`](https://github.com/cypress-io/cypress/blob/main/packages/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`](https://github.com/cypress-io/cypress/blob/main/packages/electron/src/open.ts)) to debug the main process.
- **Enable source maps** in [`tsconfig.json`](https://github.com/cypress-io/cypress/blob/main/tsconfig.json) and 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`](https://github.com/cypress-io/cypress/blob/main/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`](https://github.com/cypress-io/cypress/blob/main/packages/server/lib/browsers/cdp-protocol/cdp_automation.ts).