How to Improve Error Screenshots in Cypress
Cypress captures failures automatically in cypress run. Learn how to make screenshots more useful, preserve artifacts, and diagnose flaky failures.
Cypress already captures a screenshot when a test fails during cypress run: screenshotOnRunFailure defaults to true, and screenshots go to cypress/screenshots unless you change screenshotsFolder. Failures in the interactive cypress open app do not trigger automatic failure screenshots. To get more useful evidence, capture deliberately after the application reaches a known state, keep the right artifacts, and use retries, video, or Test Replay when a still image cannot explain what happened.
This guide covers Cypress’s built-in failure screenshots. A screenshot is diagnostic evidence, not a visual assertion: it does not compare the image to an approved baseline.
1. Check Cypress’s default failure capture
Before adding screenshot commands, confirm whether your tests run in cypress run or cypress open. Automatic failure screenshots are enabled by default in run mode. In open mode, call cy.screenshot() at a useful point if you need an image.
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
screenshotOnRunFailure: true,
screenshotsFolder: 'cypress/screenshots',
})
These are the documented defaults; writing them explicitly can make the project’s intent clear. The settings control whether and where Cypress saves images. They do not make the captured page state more informative by themselves. See Cypress’s screenshots and videos guide and configuration reference.
2. Capture a meaningful state with cy.screenshot()
For evidence tied to a particular step, first assert the application state you want to inspect, then take a named screenshot. Cypress commands are queued, so the assertion runs before the screenshot command.
// cypress/e2e/profile.cy.js
describe('profile save', () => {
it('shows the saved state', () => {
cy.visit('/profile')
cy.get('[name="displayName"]').clear().type('Taylor')
cy.contains('button', 'Save').click()
cy.contains('Saved').should('be.visible')
cy.screenshot('profile-saved-state')
})
})
This example captures a successful state for illustration. To preserve evidence around a failure, place a manual capture at a step that is reached before the failure, or rely on the automatic run failure capture. A screenshot command placed after a command that fails will not run, because the test has already stopped at that failure.
Use assertions to establish the state instead of fixed sleeps where possible. If the page depends on asynchronous data, wait for the relevant response or visible condition before capturing. Animations, pending network responses, and rendering can otherwise leave the image showing an intermediate state. Cypress describes screenshot coordination as best-effort, so even a queued capture cannot guarantee that a changing application remains unchanged until the image is taken. See the Cypress.Screenshot API and visual testing guide.
3. Choose the capture scope that answers the debugging question
| Scope | What it shows | When to use it |
|---|---|---|
viewport |
The visible application viewport | Inspect the current screen at the moment of capture. |
fullPage |
A stitched image from the top to the bottom of the page | Inspect content outside the initial viewport. |
runner |
The browser viewport and Cypress Command Log | Include runner context alongside the app. Automatic failure screenshots use runner capture by default. |
For a manual screenshot, pass the scope as an option:
cy.screenshot('long-page', { capture: 'fullPage' })
cy.screenshot('current-screen', { capture: 'viewport' })
cy.screenshot('with-runner-context', { capture: 'runner' })
Full-page screenshots are stitched while Cypress scrolls the page. Fixed or sticky headers and other fixed elements can appear more than once in the result. If the repeated element obscures the relevant content, capture the viewport or temporarily adjust the page for the diagnostic capture. Failure screenshots are coerced to runner capture; do not assume a full-page setting will change automatic failure-image scope. The API reference documents capture behavior and screenshot options.
4. Keep and locate screenshot artifacts
Cypress mirrors spec paths under its artifact folders, so a screenshot’s path can include directories for the spec. Avoid guessing a deeply nested path when automation needs the actual location. The resolved path is available from the cy.screenshot() callback, and Node event hooks such as after:screenshot and after:spec expose artifact information. See Writing and organizing tests.
One easy-to-miss setting is trashAssetsBeforeRuns, which defaults to clearing the downloads, screenshots, and videos folders before a cypress run. If a workflow depends on artifacts from earlier runs, configure artifact retention or copy the files to durable storage before the next run. Check your CI upload step as well: a screenshot saved locally is not useful to a remote reviewer if the job discards it.
5. Use retries to diagnose intermittent failures
When retries are enabled, Cypress preserves screenshots for failed attempts and adds an attempt suffix, such as (attempt 2). Compare the images across attempts:
- If the same state fails in each attempt, inspect the application behavior, assertion, test data, and underlying command error.
- If the state differs between attempts, investigate timing, network dependencies, shared data, or another source of nondeterminism.
Retries are diagnostic evidence, not a correction. A test that passes on retry can still hide an unreliable dependency or race. Cypress lets you configure retry counts separately for run mode and open mode. Review the test retries guide and configuration reference for the options available in your Cypress version.
6. Pair still images with sequence evidence
A screenshot preserves one moment. Cypress notes that its Command Log can render asynchronously, so a failure image may not yet display the error in the log. If you need to know what led to the state, retain the run video or use Test Replay where available. Use the screenshot to inspect appearance, and sequence evidence to understand timing and command history.
For CI debugging, retain the relevant screenshot, video or replay, test output, and retry attempt together. This makes it easier to distinguish a repeatable application defect from an intermittent failure without treating the image as the full record of the run.
7. Separate failure evidence from visual regression testing
cy.screenshot() captures an image; it does not compare that image to a baseline. Cypress states in its visual testing guide that “Cypress does not perform image comparison itself. The built-in cy.screenshot() command captures images but does not compare them.”
If the goal is to detect unintended UI changes, evaluate a visual-testing integration. Compare the Cypress integration and supported test modes, browser and viewport coverage, baseline storage, masking for dynamic regions, review workflow, and CI fit. Cypress documents integrations including Applitools, Chromatic, Percy, and Sauce Labs Visual; their current pricing and compatibility should be checked against your project requirements before choosing one.
8. Troubleshooting common screenshot problems
| Symptom | Likely cause | What to do |
|---|---|---|
No automatic image after a failure in cypress open |
Automatic failure capture is for cypress run. |
Call cy.screenshot() at a useful point in the test, or run the spec with cypress run. |
| No image after a run failure | screenshotOnRunFailure may be disabled, the output folder may differ, or the CI job may not retain artifacts. |
Check Cypress configuration, inspect the configured screenshots folder, and verify artifact upload and retention steps. |
| Manual screenshot is missing | An earlier command failed, preventing Cypress from reaching the screenshot command. | Move the capture before the failing action if that state is useful, and keep automatic run failure capture enabled. |
| Image shows loading or stale content | The page was captured before asynchronous rendering or data loading completed. | Wait for a meaningful assertion or network condition, then capture. Avoid relying on an arbitrary delay when a state check is possible. |
| Error is absent from the Command Log in the image | The Command Log may render asynchronously relative to screenshot capture. | Use video or Test Replay for sequence and command context; retain test output as well. |
| Sticky header appears several times in a long screenshot | Full-page capture stitches the page as Cypress scrolls. | Use viewport scope for the relevant area, or account for fixed elements in the diagnostic view. |
| Earlier artifacts disappeared | trashAssetsBeforeRuns clears artifact folders before a run by default. |
Copy artifacts to durable storage or configure cleanup and retention to match the workflow. |
| Screenshot path differs from the guessed path | Cypress mirrors spec paths under artifact folders. | Use the resolved path from the screenshot callback or Node event hooks instead of hard-coding a guessed location. |
9. Reliability and cost considerations
Reliable failure images depend on repeatable test data, stable state checks, and artifact retention. A screenshot adds an artifact to inspect and store; full-page and runner captures can include more content than a viewport image. Keep the scope matched to the debugging question, especially when CI stores artifacts for every retry.
The Cypress documentation cited here does not provide a general cost or performance benchmark for screenshot capture, so avoid assuming a particular time or storage cost. Measure the effect in your own CI workload if capture volume or artifact retention becomes a concern. For privacy, review whether captured pages can contain credentials, personal data, or other sensitive information before making artifacts broadly accessible.
10. Or skip the browser setup
If you need a clean screenshot of a page outside the Cypress run, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF, and the API accepts many parameter names used by other screenshot APIs. The ScreenshotNeo documentation has the API details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses identify the page verdict and billing status in headers.
- An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
- The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does Cypress save a screenshot every time a test fails?
By default, Cypress captures failure screenshots during cypress run. It does not automatically take failure screenshots during cypress open.
Can a Cypress screenshot prove that the page looked correct?
It records an image at a point in time. It does not compare the result with an approved baseline or prove that every preceding interaction was correct.
Should I use full-page capture for every failure?
No. Use it when content outside the viewport matters. A viewport or runner image can be easier to inspect, and full-page stitching can repeat fixed elements.
Why did a retry pass when the first attempt failed?
The test may depend on timing, changing data, or another intermittent condition. Compare attempt screenshots and sequence evidence, then investigate the cause rather than treating the retry as a fix.


