In a Cypress `cypress run`, why is there no snapshot of the failing command?
answer
- Nothing was captured, nothing was lost
- Run mode overrides what you configured
- The capture checks before it clones
- A visible browser is not an interactive session
- numTestsKeptInMemory is pinned to zero
basics
~20 sA cypress run forces numTestsKeptInMemory to zero, and Cypress only takes a snapshot when the session is interactive with a non-zero value, or when protocol capture is recording. A plain headless run meets neither, so nothing was ever captured to lose.
solid answer
~40 sThis is not eviction — it is absence. Run mode marks the session as a text terminal, and Cypress forces `numTestsKeptInMemory` to `0` when the config resolves and again after `setupNodeEvents` returns, so you cannot raise it from the config file, the CLI or a plugin. The capture step then checks two conditions before it clones anything: is this an interactive session with a non-zero `numTestsKeptInMemory`, or is protocol capture recording for the Cloud? A plain `cypress run` satisfies neither, so no snapshot object is created for any command in the spec. Note that `cypress run --headed` changes only whether you can see the browser — the session is still a text terminal, so it captures nothing either. In open mode the default is `50` tests' worth.
code
javascript · 15 linesconst { defineConfig } = require('cypress')
module.exports = defineConfig({
// ignored during `cypress run`: run mode forces this back to 0,
// both when the config resolves and again after setupNodeEvents
numTestsKeptInMemory: 20,
e2e: {
baseUrl: 'http://localhost:3000',
setupNodeEvents(on, config) {
// returning it here does not survive either
config.numTestsKeptInMemory = 20
return config
},
},
})go deeper
Know that hovering a command to see the DOM is an open-mode feature, and that a headless run gives you nothing to hover.
Explain the two mechanisms: run mode pinning the retention setting to zero, and the capture step declining to clone when nothing will consume the result.
Show how you actually debug a CI-only ticket-queue flake without the snapshot — putting the diagnosis into assertion messages and reproducing deliberately rather than hunting for an artefact that was never made.
Own the consequence for the team: a triage runbook that depends on time travel works only on a developer's machine, so decide what evidence an unattended run must produce instead.
## Absence, not eviction The single most useful thing to understand about snapshots in `cypress run` is that **nothing was captured in the first place**. It is tempting to reason by analogy with the open-mode memory limit and assume the snapshots existed and were thrown away. They were not. In a headless run the capture step never executes, so there is nothing on any command entry to go back to, at any point during the run or after it. Two mechanisms combine to produce that. ## Run mode pins the setting to zero `numTestsKeptInMemory` defaults to `50` and controls how many tests' worth of snapshots and command data the runner retains. Run mode sets the session's text-terminal flag, and when the flag is set Cypress **forces the value to `0`** — twice: 1. Once as the configuration is resolved, alongside disabling file watching. 2. Once more after `setupNodeEvents` has run, so a value returned from a plugin cannot leave a non-zero setting in the merged config. The practical consequence is that this option is not raisable in a headless run. Setting it in `cypress.config.js`, passing it on the command line, or returning it from `setupNodeEvents` all end at the same place. The second forcing exists precisely because a non-zero value in run mode used to interfere with capturing the DOM for a recorded run. ## The capture step checks two conditions Independently of the setting, Cypress only clones the DOM when something will actually consume the result. Before a log entry takes a snapshot it asks: - **Is this an interactive session with a non-zero `numTestsKeptInMemory`?** That is the time-travel case — someone can hover or pin an entry. - **Is protocol capture recording?** That is the Cloud path, where a recorded run captures the DOM for later replay by a different mechanism entirely. If neither is true, the capture returns immediately and no clone is made. A plain `cypress run` fails both tests: it is not interactive, its `numTestsKeptInMemory` is zero, and nothing is recording it. ## The trap: `--headed` does not help This is the mistake that costs an afternoon. A support-ticket spec fails only in CI, someone reasons that the browser being invisible is why they cannot see anything, and they re-run with a visible browser: ```bash npx cypress run --spec cypress/e2e/ticket-queue.cy.js --headed ``` The browser window appears, the spec runs in front of you — and there are still no snapshots. `--headed` changes only whether the browser is shown. The session is still a text terminal, so the interactive flag is still false and the setting is still pinned to zero. Watching the run happen live is genuinely useful, but it is not time travel, and hovering a command afterwards restores nothing. ## What this means for a CI-only failure A support-ticket queue that fails intermittently in CI and never locally is exactly the case where you most want to hover the failing command — and exactly the case where the snapshot is not there. That reshapes how you work: - **Do not build a triage process that assumes a snapshot exists.** If your runbook says "pin the failing command and look at the DOM", it silently only works locally. - **Put the evidence in the assertions.** A chain that asserts what it expects at each step turns the failure message itself into the diagnosis, which is the part that does survive a headless run. - **Reproducing in open mode is the local route back to snapshots** — that is where the interactive flag is true and the default of 50 applies. - **What a headless run does leave behind, and what the Cloud captures for a recorded run, are separate subjects** — worth knowing, but they are not the snapshot mechanism. | Mode | Interactive? | `numTestsKeptInMemory` | Snapshots taken? | |---|---|---|---| | `cypress open` | Yes | 50 by default | Yes | | `cypress run` | No | Forced to 0 | No | | `cypress run --headed` | No | Forced to 0 | No | | `cypress run` recorded to the Cloud | No | Forced to 0 | Captured by protocol, not for the Command Log | ## The framing that keeps you out of trouble Time travel is an **open-mode development affordance**, not a property of the runner. Treat it as something you have while you are sitting in front of the app, and treat everything that has to survive a machine you are not sitting in front of as something you must arrange deliberately — through assertion messages, through logging, or through the run's own recorded output.
- Is this eviction or absence, and why does the distinction matter?Absence. In open mode older snapshots age out once tests fall past the retained count, so a snapshot existed and was released. In `cypress run` the capture step never runs, so no snapshot object was ever created — which rules out any workaround based on retaining more of them.
- Why does `cypress run --headed` still capture no snapshots?Because the interactive flag is derived from the run-mode text-terminal flag, not from browser visibility. `--headed` only shows the browser window; the session is still a run, so `numTestsKeptInMemory` stays pinned at zero and the capture's preconditions are still unmet.
saying these in an interview costs you the question
- Says the snapshots were captured then discarded
- Suggests raising numTestsKeptInMemory in CI to get them
- Thinks --headed restores time travel
- Confuses this with the open-mode retention limit
- Assumes snapshots are written to disk with the run