Skip to content
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ _Released 11/18/2025 (PENDING)_

- Fixed an issue where [`cy.wrap()`](https://docs.cypress.io/api/commands/wrap) would cause infinite recursion and freeze the Cypress App when called with objects containing circular references. Fixes [#24715](https://github.com/cypress-io/cypress/issues/24715). Addressed in [#32917](https://github.com/cypress-io/cypress/pull/32917).
- Fixed an issue where top changes on test retries could cause attempt numbers to show up more than one time in the reporter and cause attempts to be lost in Test Replay. Addressed in [#32888](https://github.com/cypress-io/cypress/pull/32888).
- Fixed an issue where a EPIPE error shows up after CTRL+C is done in terminal. Fixes [#30659](https://github.com/cypress-io/cypress/issues/30659). Addressed in [#32873](https://github.com/cypress-io/cypress/pull/32873).

**Misc:**

Expand Down
20 changes: 18 additions & 2 deletions packages/data-context/src/data/ProjectConfigIpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,15 @@ export class ProjectConfigIpc extends EventEmitter {

let resolved = false

this._childProcess.on('error', (err) => {
this._childProcess.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EPIPE') {
debug('EPIPE error in loadConfig() of child process %s', err)

resolve()

return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: EPIPE handler resolves with wrong type

When an EPIPE error occurs in loadConfig(), the promise resolves with undefined instead of the expected LoadConfigReply object containing initialConfig and requires properties. This causes runtime errors when callers try to access these properties, such as in ProjectConfigManager.initializeConfig() which accesses loadConfigReply.initialConfig and loadConfigReply.requires.

Fix in Cursor Fix in Web

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursoragent this should be unreachable

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unable to authenticate your request. Please make sure to connect your GitHub account to Cursor. Go to Cursor

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing resolved flag prevents proper error handling

When EPIPE occurs in loadConfig(), the code calls resolve() but doesn't set resolved = true. If another error occurs afterward, handleChildProcessError will incorrectly call reject() on an already-resolved promise instead of calling onError(), causing subsequent errors to be silently ignored rather than properly reported.

Fix in Cursor Fix in Web


debug('unhandled error in child process %s', err)
this.handleChildProcessError(err, this, resolved, reject)
reject(err)
Expand Down Expand Up @@ -229,7 +237,15 @@ export class ProjectConfigIpc extends EventEmitter {
return new Promise((resolve, reject) => {
let resolved = false

this._childProcess.on('error', (err) => {
this._childProcess.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EPIPE') {
debug('EPIPE error in registerSetupIpcHandlers() of child process %s', err)

resolve()

return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: EPIPE handler resolves with wrong type

When an EPIPE error occurs in registerSetupIpcHandlers(), the promise resolves with undefined instead of the expected SetupNodeEventsReply object containing setupConfig, requires, and registrations properties. This causes runtime errors when callers try to access these properties, such as in ProjectConfigManager.handleSetupTestingTypeReply() which iterates over result.registrations.

Fix in Cursor Fix in Web

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing resolved flag prevents proper error handling

When EPIPE occurs in registerSetupIpcHandlers(), the code calls resolve() but doesn't set resolved = true. If another error occurs afterward, handleChildProcessError will incorrectly call reject() on an already-resolved promise, potentially causing unhandled promise rejection warnings. The resolved flag should be set before calling resolve().

Fix in Cursor Fix in Web


this.handleChildProcessError(err, this, resolved, reject)
reject(err)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { describe, expect, it, beforeEach, afterEach, jest } from '@jest/globals'
import { scaffoldMigrationProject as scaffoldProject } from '../helper'
import { ProjectConfigIpc } from '../../../src/data/ProjectConfigIpc'

jest.mock('debug', () => {
globalThis.debugMessages = []
const originalDebugModule = jest.requireActual('debug')

const debug = (namespace) => {
// @ts-expect-error - mock
const originalDebug = originalDebugModule(namespace)

return ((message) => {
if (namespace === 'cypress:lifecycle:ProjectConfigIpc') {
globalThis.debugMessages.push(message)
}

originalDebug(message)
})
}

debug.formatters = {}

return debug
})

describe('ProjectConfigIpc', () => {
describe('real-child-process', () => {
let projectConfigIpc

beforeEach(async () => {
const projectPath = await scaffoldProject('e2e')

projectConfigIpc = new ProjectConfigIpc(
undefined,
undefined,
projectPath,
'',
false,
(error) => {},
() => {},
() => {},
)
})

afterEach(() => {
projectConfigIpc.cleanupIpc()
})

it('EPIPE error test', async () => {
const err: NodeJS.ErrnoException = new Error

err.code = 'EPIPE'

const OG_once = projectConfigIpc.once

projectConfigIpc.once = function (evt, listener) {
if (evt === 'setupTestingType:reply') {
return listener()
}

return OG_once.apply(this, [evt, listener])
}

await projectConfigIpc.loadConfig()
await projectConfigIpc.registerSetupIpcHandlers()

projectConfigIpc._childProcess.emit('error', err)

expect(globalThis.debugMessages.at(-2)).toEqual('EPIPE error in loadConfig() of child process %s')
expect(globalThis.debugMessages.at(-1)).toEqual('EPIPE error in registerSetupIpcHandlers() of child process %s')
})
})
})