Skip to content

Commit 86d9c84

Browse files
authored
feat: exit with code 112 when api errors prevent cloud orchestrated runs (record mode, parallel run mode) (#32635)
* feat: exit with code 112 on fatal api errors when posix error codes are enabled * rm dead code * ts * system tests for 112 exit code * fix race condition w/ exit codes * only 112 on network errors, not http errors * have connection timeouts run a little bit faster in system tests * new schema for network errors * 112 should only be on network errors, update integration test * changelog * changelog * coerce boolean * code review * review * rm extra debug
1 parent c970ae2 commit 86d9c84

File tree

16 files changed

+648
-273
lines changed

16 files changed

+648
-273
lines changed

cli/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
2-
## 15.4.1
2+
## 15.5.0
33

44
_Released 10/21/2025 (PENDING)_
55

6+
**Features:**
7+
8+
- When `cypress run` is used with both `--record` and `--posix-exit-codes` enabled, Cypress will now exit with code `112` when it cannot determine which spec to run next due to network conditions. Addresses [#32485](https://github.com/cypress-io/cypress/issues/32485). Addressed in [#32635](https://github.com/cypress-io/cypress/pull/32635).
9+
610
**Bugfixes:**
711

812
- An error is no longer thrown during command execution when the application under test overwrites the `window.$` property with a non-function. Fixes [#1502](https://github.com/cypress-io/cypress/issues/1502). Fixed in [#32682](https://github.com/cypress-io/cypress/pull/32682).

packages/data-context/schemas/schema.graphql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,7 +1134,9 @@ enum ErrorTypeEnum {
11341134
CLOUD_CANNOT_CONFIRM_ARTIFACTS
11351135
CLOUD_CANNOT_CREATE_RUN_OR_INSTANCE
11361136
CLOUD_CANNOT_PROCEED_IN_PARALLEL
1137+
CLOUD_CANNOT_PROCEED_IN_PARALLEL_NETWORK
11371138
CLOUD_CANNOT_PROCEED_IN_SERIAL
1139+
CLOUD_CANNOT_PROCEED_IN_SERIAL_NETWORK
11381140
CLOUD_CANNOT_UPLOAD_ARTIFACTS
11391141
CLOUD_GRAPHQL_ERROR
11401142
CLOUD_INVALID_RUN_REQUEST

packages/errors/src/errors.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@ export const AllCypressErrors = {
173173
ciBuildId: '--ciBuildId',
174174
})}`
175175
},
176+
CLOUD_CANNOT_PROCEED_IN_PARALLEL_NETWORK: (arg1: { flags: any, response: Error }) => {
177+
const message = normalizeNetworkErrorMessage(arg1.response)
178+
179+
return errTemplate`\
180+
We encountered an unexpected error communicating with our servers.
181+
182+
${fmt.highlightSecondary(message)}
183+
184+
Because you passed the ${fmt.flag(`--parallel`)} flag, this run cannot proceed since it requires a valid response from our servers.
185+
186+
${fmt.listFlags(arg1.flags, {
187+
group: '--group',
188+
ciBuildId: '--ciBuildId',
189+
})}`
190+
},
176191
CLOUD_CANNOT_PROCEED_IN_SERIAL: (arg1: { flags: any, response: Error }) => {
177192
const message = normalizeNetworkErrorMessage(arg1.response)
178193

@@ -188,6 +203,21 @@ export const AllCypressErrors = {
188203
ciBuildId: '--ciBuildId',
189204
})}`
190205
},
206+
CLOUD_CANNOT_PROCEED_IN_SERIAL_NETWORK: (arg1: { flags: any, response: Error }) => {
207+
const message = normalizeNetworkErrorMessage(arg1.response)
208+
209+
return errTemplate`\
210+
We encountered an unexpected error communicating with our servers.
211+
212+
${fmt.highlightSecondary(message)}
213+
214+
Because you passed the ${fmt.flag(`--record`)} flag, this run cannot proceed since it requires a valid response from our servers.
215+
216+
${fmt.listFlags(arg1.flags, {
217+
group: '--group',
218+
ciBuildId: '--ciBuildId',
219+
})}`
220+
},
191221
CLOUD_UNKNOWN_INVALID_REQUEST: (arg1: { flags: any, response: Error }) => {
192222
const message = normalizeNetworkErrorMessage(arg1.response)
193223

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
We encountered an unexpected error communicating with our servers.
2+

3+
Error: fail whale
4+

5+
Because you passed the --parallel flag, this run cannot proceed since it requires a valid response from our servers.
6+

7+
The --group flag you passed was: foo
8+
The --ciBuildId flag you passed was: invalid
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
We encountered an unexpected error communicating with our servers.
2+

3+
Error: fail whale
4+

5+
Because you passed the --record flag, this run cannot proceed since it requires a valid response from our servers.
6+

7+
The --group flag you passed was: foo
8+
The --ciBuildId flag you passed was: invalid

packages/errors/test/visualSnapshotErrors.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,17 @@ describe('visual error templates', () => {
236236
}],
237237
}
238238
},
239+
CLOUD_CANNOT_PROCEED_IN_PARALLEL_NETWORK: () => {
240+
return {
241+
default: [{
242+
flags: {
243+
ciBuildId: 'invalid',
244+
group: 'foo',
245+
},
246+
response: makeErr(),
247+
}],
248+
}
249+
},
239250
CLOUD_CANNOT_PROCEED_IN_SERIAL: () => {
240251
return {
241252
default: [{
@@ -247,6 +258,17 @@ describe('visual error templates', () => {
247258
}],
248259
}
249260
},
261+
CLOUD_CANNOT_PROCEED_IN_SERIAL_NETWORK: () => {
262+
return {
263+
default: [{
264+
flags: {
265+
ciBuildId: 'invalid',
266+
group: 'foo',
267+
},
268+
response: makeErr(),
269+
}],
270+
}
271+
},
250272
CLOUD_UNKNOWN_INVALID_REQUEST: () => {
251273
return {
252274
default: [{

packages/server/__snapshots__/cypress_spec.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -422,14 +422,6 @@ exports['CLOUD_RECOMMENDATION_MESSAGE'] = `
422422
----------------------------------------------------------------------------------------------------
423423
`
424424

425-
exports['RECORD_PARAMS_WITHOUT_RECORDING-no-auto-cancel-after-failures 1'] = `
426-
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
427-
428-
These flags can only be used when recording to Cypress Cloud.
429-
430-
https://on.cypress.io/record-params-without-recording
431-
`
432-
433425
exports['RECORD_PARAMS_WITHOUT_RECORDING-auto-cancel-after-failures 1'] = `
434426
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
435427

packages/server/lib/cloud/api/index.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ const THIRTY_SECONDS = humanInterval('30 seconds')
4545
const SIXTY_SECONDS = humanInterval('60 seconds')
4646
const TWO_MINUTES = humanInterval('2 minutes')
4747

48+
function defaultTimeout () {
49+
return process.env.CYPRESS_INTERNAL_API_TIMEOUT && !isNaN(Number(process.env.CYPRESS_INTERNAL_API_TIMEOUT)) ? Number(process.env.CYPRESS_INTERNAL_API_TIMEOUT) : SIXTY_SECONDS
50+
}
51+
4852
function retryDelays (): number[] {
4953
return process.env.API_RETRY_INTERVALS
5054
? process.env.API_RETRY_INTERVALS.split(',').map(_.toNumber)
@@ -413,7 +417,7 @@ export default {
413417
url: recordRoutes.runs(),
414418
json: true,
415419
encrypt: preflightResult.encrypt,
416-
timeout: options.timeout ?? SIXTY_SECONDS,
420+
timeout: options.timeout ?? defaultTimeout(),
417421
headers: {
418422
'x-route-version': '4',
419423
'x-cypress-request-attempt': attemptIndex,
@@ -487,7 +491,7 @@ export default {
487491
url: recordRoutes.instances(runId),
488492
json: true,
489493
encrypt: preflightResult.encrypt,
490-
timeout: timeout ?? SIXTY_SECONDS,
494+
timeout: timeout ?? defaultTimeout(),
491495
headers: {
492496
'x-route-version': '5',
493497
'x-cypress-run-id': runId,
@@ -507,7 +511,7 @@ export default {
507511
url: recordRoutes.instanceTests(instanceId),
508512
json: true,
509513
encrypt: preflightResult.encrypt,
510-
timeout: timeout ?? SIXTY_SECONDS,
514+
timeout: timeout ?? defaultTimeout(),
511515
headers: {
512516
'x-route-version': '1',
513517
'x-cypress-run-id': runId,
@@ -525,7 +529,7 @@ export default {
525529
return rp.put({
526530
url: recordRoutes.instanceStdout(options.instanceId),
527531
json: true,
528-
timeout: options.timeout ?? SIXTY_SECONDS,
532+
timeout: options.timeout ?? defaultTimeout(),
529533
body: {
530534
stdout: options.stdout,
531535
},
@@ -547,7 +551,7 @@ export default {
547551
return rp.put({
548552
url: recordRoutes.instanceArtifacts(options.instanceId),
549553
json: true,
550-
timeout: options.timeout ?? SIXTY_SECONDS,
554+
timeout: options.timeout ?? defaultTimeout(),
551555
body,
552556
headers: {
553557
'x-route-version': '1',
@@ -566,7 +570,7 @@ export default {
566570
url: recordRoutes.instanceResults(options.instanceId),
567571
json: true,
568572
encrypt: preflightResult.encrypt,
569-
timeout: options.timeout ?? SIXTY_SECONDS,
573+
timeout: options.timeout ?? defaultTimeout(),
570574
headers: {
571575
'x-route-version': '1',
572576
'x-cypress-run-id': options.runId,

0 commit comments

Comments
 (0)