In Cypress 16, why does `Cypress.Commands.overwrite('getCookie', fn)` now throw?
answer
- Something about those commands changed in 16
- They retry now, like other queries
- Two override APIs, guarded against each other
- The replacement API ends in Query
- originalFn returns a function here
basics
~10 sBecause cy.getCookie() became a retry-able query in Cypress 16. Cypress.Commands.overwrite() replaces commands only, so it refuses the name and tells you to use Cypress.Commands.overwriteQuery() instead, which takes a different callback shape.
solid answer
~40 sCypress 16 converted `cy.getCookie()`, `cy.getCookies()`, `cy.getAllCookies()`, `cy.getAllLocalStorage()` and `cy.getAllSessionStorage()` into **queries**: they re-read state and retry any chained assertions until those pass or `defaultCommandTimeout` expires. Commands and queries have separate override APIs, and Cypress guards both directions — `Cypress.Commands.overwrite()` refuses a name that now belongs to a query and points you at `Cypress.Commands.overwriteQuery()`, while `overwriteQuery` refuses a plain command and points you back. The migration is more than a rename, because the callback contract differs. A query callback is a function that *returns* a function, so `overwriteQuery` hands you an `originalFn` that itself returns the inner subject-to-subject step. Invoke it as `originalFn.apply(this, args)` — query callbacks rely on `this` — and wrap either the outer arguments or the inner result.
go deeper
Remember that Cypress has two override APIs, one for commands and one for queries, and that picking the wrong one fails loudly rather than silently.
Explain what makes a query different from a command — synchronous, retried, subject-to-subject — and why that forces a different callback shape on the override API.
Talk through the upgrade as an operator: how you find every affected override, which ones you delete rather than port, and how you verify the ported ones on a real spec.
Own the policy on overriding vendor built-ins at all, since each one is a standing bet on internals that a major release can revoke across every spec at once.
## What changed in Cypress 16 Cypress 16, released on 1 September 2026, promoted five state-reading commands to **queries**: - `cy.getCookie()`, `cy.getCookies()` and `cy.getAllCookies()` - `cy.getAllLocalStorage()` and `cy.getAllSessionStorage()` As queries they re-read the underlying state and retry chained assertions until those pass or the timeout expires, they are governed by `defaultCommandTimeout`, and — the part that breaks support files on upgrade — **they can no longer be overwritten with `Cypress.Commands.overwrite()`**. A suite that wrapped `cy.getCookie()` to normalise an expense-portal session cookie stops working the moment the support file loads, before any spec runs. ## Why the two APIs are separate A command and a query are different kinds of thing, so their override APIs cannot be interchangeable: - A **command** callback runs once. It may be asynchronous, it may change the application, and Cypress executes it exactly one time. - A **query** callback is synchronous and runs in two halves. The outer half runs once; the inner half maps a previous subject to a new subject and is invoked repeatedly, which is what gives a query its retries. Cypress therefore keeps a register of each and checks the name before it accepts an override. Overwriting a query with the command API throws, saying that queries can only be overwritten with `Cypress.Commands.overwriteQuery()`; the mirror-image mistake — `overwriteQuery` on a plain command such as `cy.request()` — throws in the same way, saying commands can only be overwritten with `Cypress.Commands.overwrite()`. Both errors fire at registration time, not at the call site. ## The `overwriteQuery` callback shape `Cypress.Commands.overwriteQuery(name, callbackFn)` prepends the original query implementation to your arguments, exactly as the command API prepends `originalFn`. The difference is what `originalFn` *is*: it is a function that returns a function, so you have two places to intervene. ```javascript Cypress.Commands.overwriteQuery('getCookie', function (originalFn, name, options) { // outer half: runs once, sees the arguments the test passed const innerFn = originalFn.apply(this, [name, options]) return (subject) => { // inner half: runs on every retry, sees and returns the subject return innerFn(subject) } }) ``` Two details matter here and both are easy to miss: - **Use `.call()` or `.apply()`.** Query callbacks rely on `this` — that is where a query's timeout is set — so calling `originalFn(name, options)` bare loses the binding. - **Do not use an arrow function** for the outer callback, for the same reason. ## Migrating an existing overwrite 1. **Find them.** Grep the support file for `Cypress.Commands.overwrite(` and check each name against the five converted ones. The upgrade error names one at a time, so a suite with three affected overwrites takes three runs to discover them all if you do not look first. 2. **Decide whether you still need the override.** Some pre-16 overwrites existed only to bolt retries onto a cookie read by hand. Now that the built-in retries, deleting the override is often the whole migration. 3. **Rename the API and reshape the callback.** Switch to `Cypress.Commands.overwriteQuery`, change the arrow to a `function`, and return a function from it rather than a value. 4. **Decide which half your logic belongs in.** Argument massaging goes in the outer half so it happens once; anything that inspects or rewrites the yielded value goes in the inner half so it happens on every retry. 5. **Re-run a spec that asserts on the overridden step**, because a query that returns the wrong shape fails as a timeout rather than as a type error, which reads confusingly in CI. ## Blast radius on a real suite This is a support-file change, so it lands on every spec at once. Two habits keep the upgrade cheap: keep overrides of built-ins few enough to enumerate in a single screen, and prefer a differently-named custom command for behaviour that is yours rather than Cypress's — a `cy.sessionCookie()` of your own would have survived this release untouched, because nothing about Cypress's own retry model applies to a name Cypress does not own.
- What happens in Cypress if you call `Cypress.Commands.overwriteQuery()` on a command such as `cy.request()`?Cypress throws the mirror-image error at registration, saying that command can only be overwritten with `Cypress.Commands.overwrite()`. The two registers are checked against each other in both directions, so you cannot smuggle a command into the query API or the reverse — which is useful, because the callback contracts are incompatible and a silent acceptance would fail much later and much less clearly.
- After the Cypress 16 change, do those cookie and storage reads need a different timeout option?They accept a `timeout` option and are governed by `defaultCommandTimeout` like other queries, so a suite that previously wrapped them in bespoke waiting can usually drop that code. The practical consequence is that an assertion chained onto `cy.getAllLocalStorage()` now keeps re-reading storage instead of failing against the first snapshot it took.
saying these in an interview costs you the question
- Says the fix is only renaming overwrite to overwriteQuery
- Calls originalFn without .call or .apply
- Writes the overwriteQuery callback as an arrow function
- Believes overwrite still works on any built-in name
- Thinks the error appears at the call site, not at registration