skip to content

How far should a Cypress port raise defaultCommandTimeout to absorb old waits?

level: principalimportance: should knowfreq 36%

answer

  1. Ask who pays for a longer timeout
  2. A green run finishes early either way
  3. The timeout states an expectation
  4. One knob does not cover every command
  5. Per-command option beats a global raise

basics

~20 s

Barely at all. The 4000 ms default costs nothing on a passing run but is paid in full on every failure, so a global 30-second budget buys slow red builds and hidden regressions. Raise it per command instead.

solid answer

~50 s

Start from what the raise actually costs. Retry-ability finishes a command as soon as its condition holds, so a larger `defaultCommandTimeout` is free on a green run; the whole cost lands on the red path, where every failing command burns the full budget and a broken spec takes minutes to say so. The second cost is silence — the timeout is the only place a suite states a performance expectation, and a 30-second global says the team has none. Cypress's own guidance is not to change it globally but to pass `{ timeout: ms }` on the individual slow command. So set the global from the application's measured worst case rather than from the old suite's biggest explicit wait, raise it per command where a step is genuinely slow with a comment saying why, and convert the waits that were really network waits into `cy.intercept()` and `cy.wait('@alias')`.

go deeper

for a junior

Know that defaultCommandTimeout is 4000 ms and bounds how long a Cypress command retries, and that a single slow command can be given its own timeout option.

for a middle

Explain that retry-ability exits as soon as the condition holds, so a bigger budget costs nothing on green and everything on red, and name the separate timeouts that cover page loads, aliased waits and tasks.

for a senior

Show that you read a required timeout as evidence about the application rather than a setting to tune, and that you would rewrite a timed wait into an aliased request before reaching for a larger number.

for a principal

Own the suite's timeout policy: the measurement the global is derived from, where per-command exceptions are allowed, and the dated owner of any migration-time raise. Be ready to argue that a large global timeout is a deleted performance expectation.

## What a bigger timeout actually buys `defaultCommandTimeout` is **4000 ms** by default and bounds how long most DOM-based commands retry. The reflex during a port is to set it to the largest explicit wait in the old suite — thirty seconds, sometimes sixty — so that nothing times out while the team is busy translating. It is worth being precise about what that buys, because the intuition most people carry is wrong in both directions. - It does **not** slow down a passing run. Retry-ability finishes each command as soon as its condition holds, so the timeout is a ceiling, never a duration. A green suite runs at the same speed at 4000 ms and at 30000 ms. - It does **not** make anything more reliable. A step that needs thirty seconds today needs thirty-one tomorrow; the number is a guess about the application, restated. - It **does** buy quiet during the migration. Genuinely broken translations stop shouting, which is occasionally what you want and usually what you regret. ## What it costs, and who pays The whole cost lands on the red path, which is exactly where feedback matters most. 1. **Failure feedback time.** Every failing command burns the full budget. A spec with five broken steps takes two and a half minutes to fail at 30000 ms and twenty seconds at 4000 ms, and a migration produces a lot of broken steps. 2. **Masked regressions.** The timeout is the only place a suite states a performance expectation. A screen that regressed from 400 ms to 12 seconds still passes under a 30-second global, and the suite that was supposed to notice says nothing. 3. **Lost signal about the port itself.** During a migration the useful information is *which* translations are wrong. A large global budget converts that signal into a slow, uniformly green run followed by a slow, uniformly red one. Cypress's own guidance is unambiguous: do not change the command timeout globally; pass the individual command's `{ timeout: ms }` option instead. ## One knob does not cover the suite A team that raises `defaultCommandTimeout` and still sees timeouts is usually turning the wrong knob. | Command | Timeout that governs it | Default | |---|---|---| | most DOM commands and their assertions | `defaultCommandTimeout` | 4000 ms | | `cy.visit()`, `cy.go()`, `cy.reload()` | `pageLoadTimeout` | 60000 ms | | `cy.wait()` on a route alias | `requestTimeout` / `responseTimeout` | 5000 ms / 30000 ms | | `cy.task()` | `taskTimeout` | 60000 ms | ## Where the line goes 1. **Set the global from the application, not from the old suite.** Measure the worst case for a normal interaction on the slowest environment the suite runs against, add headroom, and stop. The old suite's biggest explicit wait is evidence about a different runner, not about your app. 2. **Raise per command where a step is genuinely slow**, with `{ timeout: ms }` and a comment saying what makes it slow. That keeps the exception visible and reviewable; a global raise makes it invisible. 3. **Alias the request when the wait is really a network wait.** `cy.intercept()` plus `cy.wait('@alias')` is deterministic, so it needs no headroom at all. 4. **Treat a step that needs a large timeout and has no request to alias as a product question.** Something takes twelve seconds; the test is the messenger. ## The one defensible temporary raise There is a real case for a large global timeout on the **first pass** of a big port: you want to see which specs are genuinely broken rather than drown in timeouts caused by translations you have not written yet. The condition that makes it defensible is that it is dated and owned. Land it as a tracked, time-boxed concession with a named owner, then walk it back in steps — drop the global, add `{ timeout }` to each command that then fails, and treat every one of those as a question about the application. What remains when you finish is a short performance backlog, which is a far more useful artefact than a single large number in the configuration file. ## What to write down - The value the global is set to, and the measurement that justifies it. - Which commands carry a per-command timeout and why. - The rule that a network wait is aliased rather than absorbed by a timeout. - The date a migration-time raise comes back down, and who owns it.

  • Which Cypress commands does defaultCommandTimeout not govern?
    Page transitions use `pageLoadTimeout`, 60000 ms, for `cy.visit()`, `cy.go()` and `cy.reload()`. A `cy.wait()` on a route alias uses `requestTimeout` at 5000 ms and `responseTimeout` at 30000 ms. `cy.task()` uses `taskTimeout` at 60000 ms. Raising the command timeout changes none of those.
  • How do you walk a migration-time global raise back down without a week of red builds?
    Lower it in steps and let the failures name themselves. Drop the global, add `{ timeout: ms }` to each command that then fails, and treat every one as a question about the application rather than about the test. What is left when you stop is a short performance backlog, which is more actionable than one large number in the config.

saying these in an interview costs you the question

  • Sets the global timeout to the old suite's largest wait
  • Says a longer timeout slows down passing tests
  • Assumes defaultCommandTimeout also covers cy.visit()
  • Treats a timeout raise as free because CI runs unattended
  • Leaves a migration-time raise in place with no owner or date