In Cypress 16, what replaces the removed cy.end() command?
answer
- Removed, with nothing in its place
- It only yielded null
- A new cy call already starts a chain
- Deprecated in 15.15.0, removed in 16.0
- Delete the calls when upgrading
basics
~20 sNothing replaces it. cy.end() ended a chain by yielding null, which was never needed, because a Cypress chain is already terminated as soon as the next cy command starts a new one. In Cypress 16 you simply delete the calls.
solid answer
~40 s`cy.end()` was removed in Cypress 16 and has **no replacement** — you delete the calls. It existed to end a chain by yielding `null`, so that whatever followed did not keep working from the previous step's subject. That was never necessary: a statement beginning with `cy.<command>()` already starts a fresh chain, carrying nothing from the last one. So `cy.get('[data-cy="borrow"]').click().end()` and `cy.get('[data-cy="borrow"]').click()` queue the same steps apart from one no-op, and only the second is valid in Cypress 16. The command was deprecated first, in Cypress 15.15.0, then removed in 16.0. If an older suite still calls it, the fix is mechanical — strip `.end()` wherever it appears; nothing about the queue's ordering changes.
code
javascript · 10 linesit('borrows a book and sets a return date', () => {
cy.visit('/catalogue/books/dune')
// Cypress 15 and earlier accepted .end() here, but it was never needed:
// cy.get('[data-cy="borrow"]').click().end()
// Cypress 16: delete it. The next cy.<command>() already starts a fresh chain.
cy.get('[data-cy="borrow"]').click()
cy.get('[data-cy="return-by"]').type('2026-10-01')
})go deeper
Just know that cy.end() is gone in Cypress 16 and was never needed. If you see it in an old spec or an older tutorial, delete it.
Be able to say what it did — yield null to end a chain — and why that was redundant, since a statement starting with a new cy command already begins a chain with no carried subject.
Expect it inside an upgrade story. Treat it as the cheap, mechanical part of moving a suite to Cypress 16, and be ready to say how you would find every call site and confirm the run starts clean afterwards.
Own the upgrade posture: which removed APIs a codebase can strip mechanically and which need judgement, and how the team keeps removed calls from re-entering specs after the migration.
## What `cy.end()` used to do `cy.end()` was a command whose only job was to **end a chain by yielding `null`**. Written as `.end()` at the tail of a chain, it produced a step that carried no subject forward, so anything appended after it started from nothing rather than from the element the previous step had been working with. People used it as punctuation. A chain that had clicked a borrow button would be closed with `.end()` before the next line went off to do something unrelated, on the theory that the chain had to be explicitly finished. ## Why it was never necessary It was not. A Cypress chain is **already terminated when the next `cy.<command>()` starts a new one**. Statements that begin with `cy.` are independent chains; nothing from the previous statement is carried into them. So the terminator closed something that had already closed itself. That means these two fragments queue exactly the same steps, minus one no-op: | Cypress 15 and earlier | Cypress 16 | |---|---| | `cy.get('[data-cy="borrow"]').click().end()` | `cy.get('[data-cy="borrow"]').click()` | | `cy.get('[data-cy="return-by"]').type('2026-10-01')` | `cy.get('[data-cy="return-by"]').type('2026-10-01')` | Cypress deprecated the command in **15.15.0** with exactly that guidance — instead of using `.end()` to break a chain, start a new chain off `cy` — and removed it in **16.0**. ## The upgrade: delete the calls As of Cypress 16 there is **no replacement command**. The migration is the shortest kind there is: 1. Find every `.end()` call in the suite. 2. Delete it, leaving the rest of the chain untouched. 3. Run the suite. Nothing else needs to change, because the queue's contents are identical apart from the removed no-op. Calling it now fails rather than warning, so a spec that still contains it does not silently keep working. ## What this does not change It is worth being explicit about the things a candidate sometimes assumes moved with it: - **Ordering is unaffected.** `.end()` was a queued step like any other; removing it takes one entry out of the queue and leaves every other step in its position. - **Nothing else terminates a chain differently.** Chaining still carries a subject along one statement, and a new statement starting with `cy.<command>()` still begins with none. - **`.then()` did not inherit the job.** It is a queued step that takes a callback; it was never a terminator, and it is not one now. - **There is no configuration flag to bring it back.** The removal is not behind an option. ## Why a removal like this is worth noticing The interesting part of `cy.end()` is not the command; it is what its existence and removal say about the queue. It survived for years because the two-phase model is easy to misread: if you think of a chain as something that runs as you write it, a way to say "and I am finished with that" feels necessary. Once you hold the actual model — the body appends steps, the runner drains them, and a statement beginning with `cy.` starts a fresh chain — the command has nothing left to do. That is also why the upgrade is safe to do mechanically while other Cypress 16 removals are not. Deleting `.end()` cannot change behaviour, because the step it removed had no behaviour to change. It is worth separating that class of change from the class that needs judgement when you plan a version upgrade for a suite someone else has to keep green.
- Does removing cy.end() change the order in which a Cypress test's commands run?No. `.end()` was itself a queued step that yielded `null`, so deleting it removes one no-op from the queue and nothing else. The remaining steps keep their positions and the drain order is identical, which is why the upgrade guidance is a plain deletion rather than a rewrite.
- In Cypress 16, what carries a subject from one line of a test to the next?Only chaining within a single statement. `cy.get(...).find(...).click()` carries a subject along that chain and stops at the end of the statement. A new statement beginning with `cy.<command>()` starts from no subject at all, which is exactly why an explicit terminator was redundant.
saying these in an interview costs you the question
- Still writes .end() to close a Cypress chain
- Thinks cy.end() is needed before starting a new chain
- Says cy.end() was renamed rather than removed
- Expects a replacement command for cy.end() in Cypress 16