Skip to content

Fix crash in after:spec when reloading after the last test - #1365

Open
PravinPatil17 wants to merge 2 commits into
badeball:masterfrom
PravinPatil17:fix/after-spec-has-reloaded-state
Open

Fix crash in after:spec when reloading after the last test#1365
PravinPatil17 wants to merge 2 commits into
badeball:masterfrom
PravinPatil17:fix/after-spec-has-reloaded-state

Conversation

@PravinPatil17

Copy link
Copy Markdown

Reload-behavior on Cypress <15.18.0 re-fires before:spec. When the reload occurs once every test body has already finished (typically in an after() / afterEach() hook), the plugin enters a "has-reloaded" state but no test re-runs, so after:spec fires directly. afterSpecHandler did not handle this state (nor its "has-reloaded-received-envelopes" sibling) and threw:

Unexpected state in afterSpecHandler: has-reloaded

which surfaced to users as a misleading "some other plugin is overwriting this plugin's event handlers" error.

Both states already carry the completed messages in messages.current, so they now flow through the normal report-generation path instead of throwing.

Adds a regression feature (gated to Cypress <15.18.0, where the state is reachable) plus an isPre15_18 helper and matching cucumber tag hook.

@badeball

Copy link
Copy Markdown
Owner

Your added test passes in the master branch, ie. I can't reproduce your issue. Furthermore, almost the exact scenario is already tested here and here.

@PravinPatil17

PravinPatil17 commented Jul 10, 2026

Copy link
Copy Markdown
Author

Your added test passes in the master branch, ie. I can't reproduce your issue. Furthermore, almost the exact scenario is already tested here and here.

You're right — the feature test I first added passes on master; I've removed it. I had the wrong trigger. Here's the actual one, plus a deterministic reproduction you can run.

The reload isn't hook-driven. It's a mid/late-spec Electron GPU/renderer crash (GPU process exited unexpectedly: exit_code=15) under memory pressure: Cypress recovers by re-firing before:spec after the last scenario already finished, so the state machine goes test-finished → has-reloaded, and since nothing re-runs (no specEnvelopes re-emitted), after:spec fires while still in has-reloaded and throws. This is field-confirmed on cypress@15.15.0 + preprocessor 24.0.1, on specs with >10 scenarios; all scenarios pass and the job only fails in afterSpecHandler.

This is really the case the browserCrashExprCol guard already tries to cover — but that guard only matches We detected that the … process just crashed. A GPU-process crash recovered mid-spec leaves results.error as null by after:spec (all tests ultimately passed), so the guard is skipped and the switch throws.

Since the crash isn't scriptable in a cucumber feature, I've added a handler-level test (lib/plugin-event-handlers.test.ts, gated to Cypress <15.18.0 where has-reloaded is reachable) that drives beforeRun → beforeSpec → specEnvelopes → testCaseStarted → testCaseFinished → beforeSpec (crash re-fire) → afterSpec → afterRun. It fails on master with the exact error and passes with this change, and asserts the completed scenario is preserved in the report.

@PravinPatil17
PravinPatil17 force-pushed the fix/after-spec-has-reloaded-state branch from ec6ebaa to ec4b8f2 Compare July 10, 2026 15:23
@badeball

badeball commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Here's the actual one, plus a deterministic reproduction you can run.

Unfortunately, your test is nothing I'd like to add to this repository. It fails to test any Cypress behavior at all. It test's an assumption, something I can't verify. Nor does it catch any regression or improvement in current behavior.

This is field-confirmed on cypress@15.15.0 + preprocessor 24.0.1, on specs with >10 scenarios; all scenarios pass and the job only fails in afterSpecHandler.

Why don't you share a little bit about this? How can I reproduce the issue?

Reload-behavior on Cypress <15.18.0 re-fires before:spec. When the reload
happens after every test body has already finished -- eg. a browser (GPU /
renderer) crash that Cypress recovers from by re-firing before:spec -- the
state machine enters "has-reloaded" but no test re-runs, so after:spec fires
directly. afterSpecHandler did not handle this state (nor its sibling
"has-reloaded-received-envelopes") and threw:

    Unexpected state in afterSpecHandler: has-reloaded

which is surfaced to users as a misleading "some other plugin is overwriting
this plugin's event handlers" error.

Both states already carry the completed messages in `messages.current`, so
they now flow through the normal report-generation path -- the report is
preserved rather than thrown away.

Adds a handler-level regression test (gated to Cypress <15.18.0, the only
versions where "has-reloaded" is reachable) that drives the crash-recovery
sequence and asserts after:spec doesn't throw and the completed scenario is
preserved in the report. It fails on master and passes with this change.

Because the real-world trigger (a non-deterministic browser crash) can't be
scripted in a .feature, debug/repro-has-reloaded.js reproduces the exact
resulting event sequence against the compiled state machine for debugging.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@PravinPatil17
PravinPatil17 force-pushed the fix/after-spec-has-reloaded-state branch from 636f8b3 to 20fe235 Compare July 13, 2026 05:48
@PravinPatil17

Copy link
Copy Markdown
Author

Here's the actual one, plus a deterministic reproduction you can run.

Unfortunately, your test is nothing I'd like to add to this repository. It fails to test any Cypress behavior at all. It test's an assumption, something I can't verify. Nor does it catch any regression or improvement in current behavior.

This is field-confirmed on cypress@15.15.0 + preprocessor 24.0.1, on specs with >10 scenarios; all scenarios pass and the job only fails in afterSpecHandler.

Why don't you share a little bit about this? How can I reproduce the issue?

i have added the repro readme please refer

@badeball

badeball commented Jul 13, 2026

Copy link
Copy Markdown
Owner

You're misunderstanding what I mean by a reproducible example. This still doesn't illustrate Cypress' behavior and it doesn't allow me to get a better understanding of the error. If I can't reproduce the behavior, then I can't fix it.

This is field-confirmed on cypress@15.15.0 + preprocessor 24.0.1, on specs with >10 scenarios; all scenarios pass and the job only fails in afterSpecHandler.

Can you share this field test that lets me observe the erroneous behavior?

debug/repro-cypress/ is a minimal, drop-in Cypress project that reproduces the
after:spec has-reloaded crash in a real Cypress run. A crashMode env switch
covers two modes:

  - cdp (default): real navigation + a deterministic GPU-process crash via CDP
    (Browser.crashGpuProcess). Reliable on any machine.
  - memory: self-contained heavy page that exhausts GPU memory with WebGL until
    the GPU process crashes on its own -- the raw field scenario.

A GPU-process crash is used deliberately: renderer crashes are already caught by
afterSpecHandler's browserCrashExprCol guard, whereas a GPU crash slips through
to the unhandled has-reloaded state (field signature "GPU process exited
unexpectedly: exit_code=15").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@PravinPatil17

Copy link
Copy Markdown
Author

You're misunderstanding what I mean by a reproducible example. This still doesn't illustrate Cypress' behavior and it doesn't allow me to get a better understanding of the error. If I can't reproduce the behavior, then I can't fix it.

This is field-confirmed on cypress@15.15.0 + preprocessor 24.0.1, on specs with >10 scenarios; all scenarios pass and the job only fails in afterSpecHandler.

Can you share this field test that lets me observe the erroneous behavior?

have added repro project , can you please give it a try

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants