skip to content

When a Cypress command fails, what happens to the commands still queued behind it?

level: seniorimportance: must knowfreq 56%

answer

  1. Failure is terminal
  2. The rest of the queue is abandoned
  3. No error handler on a command
  4. Eventually passes, or the test fails
  5. Make the outcome certain, do not branch

basics

~20 s

They never run. A failed Cypress command ends the test — everything still queued behind it is abandoned and the test is reported failed. Cypress offers no catch handler on a command, so a step cannot recover and carry on.

solid answer

~40 s

A Cypress command either eventually passes or it fails, and failure is terminal for that test. When a step exhausts its timeout or throws, Cypress stops draining: the remaining steps in that test's queue are discarded and the test is marked failed. There is deliberately **no `.catch()`** to attach to a command and no built-in error recovery. That rules out the pattern people reach for first — get `[data-cy="borrow"]` and, if it is not there, do something else — because the same run could then take either path. A JavaScript `try/catch` around the chain does not help either; the body has already returned before any step runs. Where an outcome is uncertain, make it certain: seed the catalogue so the borrow button is always there, rather than asking the test to choose at runtime.

go deeper

for a junior

Remember the simple rule: if a Cypress command fails, the test stops there and is reported failed, and nothing queued after it runs.

for a middle

Be able to explain why there is no error handler on a command — recovery is a branch, and a branch on failure lets the same test take different paths on different runs, which is the flake the tool exists to remove.

for a senior

Expect a scenario. Show how you would remove the uncertainty rather than catch it: control the data the catalogue starts from, and read a red run knowing the failing step is simply the last one that ran.

for a principal

Own the consequence for the suite: a model with no per-step recovery pushes the burden onto test data and environment control. Be ready to say where your team invests to make outcomes deterministic instead of defensive.

## What "fails" means for a queued command A queued Cypress step has exactly two outcomes. It **eventually passes**, or it **fails** — because its timeout expired, because an assertion attached to it never became true, or because the code inside a callback threw. There is no third outcome in which the step gives up quietly and the test carries on. Failure is terminal for the test that owns the queue. The moment a step fails, Cypress stops draining: every step still queued for that test is abandoned, and the test is reported as failed. The documentation states it directly — a command eventually passes, or if it fails, all remaining commands are not executed and the test as a whole fails. ## The scope of the abandonment It is worth being precise about how far the damage reaches, because candidates routinely over-state it: - **The remaining steps in that test** are abandoned. They never run. - **That one test** is reported failed. - **The other tests in the spec** are not abandoned. The runner moves on to the next one. - **A step that already ran** is not undone. Whatever it did to the application stands, which is why a half-completed borrow can leave the catalogue in a state the next test has to cope with. ## Why there is no `.catch()` Cypress deliberately exposes no error handler on a command. You cannot write `cy.get('[data-cy="borrow"]').catch(...)`, and there is no built-in recovery from a failed step. The reason is the same one behind serial execution: recovery is a branch, and a branch taken on whether something failed makes the same test able to do different things on different runs. A suite built that way stops being evidence about the application, because a green result no longer tells you which path ran. The other half of the answer is where people are most often wrong: **a JavaScript `try/catch` around the chain does not help either.** The chain is built during the body phase, so the `try` block has already exited by the time any step runs. The failure surfaces later, from the runner's own drain loop, with nothing left on the stack for `catch` to intercept. | what you expect from a promise chain | what a Cypress queue does | |---|---| | `.catch()` handles a rejection and the chain continues | no handler exists; the test ends | | `try/catch` around an awaited call traps the error | the body has returned; nothing is trapped | | a failed link can be retried by your own code | the failing step decides the test's fate | | you can branch on success or failure | the same run must take the same path every time | ## What to do instead of catching The move is to make the outcome certain before the queue reaches it, rather than reacting to it: 1. **Control the starting data.** If the test needs a borrowable copy of a book, seed the catalogue with one rather than asking the test to discover whether a borrow button happens to be there. 2. **Assert the state you depend on.** A step that states the expectation — that the results list holds a row for the title you searched — fails on the real problem, with a message naming it, instead of a later step failing for a reason two layers away. 3. **Split the branch into two tests.** "A member with no loans sees Borrow" and "a member at their limit sees the limit notice" are two deterministic specs, each of which knows which state it set up. 4. **Keep genuinely uncertain checks out of the automated path.** If nobody can say in advance which of two things the page will show, that uncertainty is a product question before it is a test question. ## Reading a failure the queue produced Because the queue stops dead, the failing step is by definition **the last step that ran**, and that is the single most useful fact when you are handed a red run: - Everything **before** it completed successfully, so the application really did reach that state. - Everything **after** it is unproven — not failing, just never attempted. Resist reading the absence of later failures as evidence that later behaviour works. - If the failing step is a query that timed out, the interesting question is usually about the step *before* it — what it left the page in, or failed to trigger — not about the selector that reported the error. That reframing is most of the skill: the command that reported the failure is where the queue stopped, not necessarily where the defect is.

  • Can you wrap a Cypress chain in a JavaScript try/catch to recover from a failing command?
    No. The chain only enqueues during the body phase, so the `try` block has already exited by the time any command runs, and the failure surfaces later from Cypress's own drain loop. There is nothing on the stack for `catch` to intercept, and Cypress exposes no per-command error handler in its place.
  • If a Cypress test fails at its third command, are the spec's remaining tests abandoned too?
    No. The abandonment is scoped to the failing test: its own queue is discarded and it is reported failed, then the runner moves on to the next test in the spec. Only that one test's remaining steps are lost — though whatever the earlier steps already did to the application is not undone.

saying these in an interview costs you the question

  • Says you can attach .catch() to a Cypress command
  • Thinks a failing command is skipped and the test continues
  • Expects try/catch around a chain to trap a command failure
  • Believes one failed command aborts the whole spec file
  • Wants if/else on whether a Cypress query found an element