Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
An EPERM from Cypress is not one single bug. First read the complete error and identify the operation (mkdir, image write, unlink, or rename) and the exact path. Then check the operating system, Cypress version, process account, and the configuration loaded by that run. A creation/write failure needs a writable destination; an unlink or cleanup failure concerns an old screenshot tree and may involve Cypress’s pre-run asset deletion.
Cypress stores manual screenshots and failure screenshots below screenshotsFolder, whose documented default is cypress/screenshots (configuration reference). The sections below give a safe way to change that folder, distinguish cleanup errors from capture errors, and verify where files actually land.
1. Capture the evidence before changing settings
Do not start by repeatedly changing the folder. Save the entire EPERM line, including the verb and path, and note:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →- Operating system and whether the run is local, containerized, or CI.
- Cypress version and the command used (
cypress openversuscypress run). - The configured
screenshotsFoldervalue and the configuration file that was loaded. - The spec being run and whether the path contains nested directories, spaces, a network share, or a synchronized folder.
- Whether the named operation is creating a directory, writing an image, deleting an existing asset, or renaming a temporary file.
These distinctions matter. Setting a different destination cannot repair an EPERM while deleting the previous destination, and disabling cleanup cannot make a protected destination writable.
#1 Best Overall
2. Configure a writable screenshot destination
Cypress 10 and later
Set screenshotsFolder in the configuration used to start the run. A project-relative directory is usually easiest to reason about:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
screenshotsFolder: 'artifacts/cypress-screenshots',
e2e: {
baseUrl: 'http://localhost:3000'
}
})
Use the equivalent TypeScript or ECMAScript-module syntax if your project does. The important point is that the setting belongs in the loaded Cypress configuration, not in an individual test. Confirm the value in the same environment and command that produces the error.
Older Cypress configuration
Projects using the older JSON configuration can set the same key in cypress.json:
{
"screenshotsFolder": "artifacts/cypress-screenshots"
}
Do not assume a path is writable merely because your interactive shell can write there. Cypress may run as a different account in a service, container, or CI job. Check the parent directory’s permissions as that account and ensure the parent can be created.
How Cypress builds paths beneath the root
screenshotsFolder is a root, not necessarily the final image directory. Cypress derives subdirectories from the spec path, and cy.screenshot() also supports nested paths in the screenshot name (screenshot command documentation). For example:
Rank #2
cy.screenshot('checkout/payment/declined')
can require artifacts/cypress-screenshots/checkout/payment to be created before the image is written. A permission check on only the root can therefore miss a denied or read-only nested directory.
3. Separate capture failures from pre-run cleanup failures
When the error names mkdir, write, or a temporary rename
Inspect every component of the destination. Remove read-only or protected locations, avoid placing the folder inside a system directory, and test with a short project-relative path. If CI uses a service account, grant that account access or choose a workspace directory it owns. Also check free space and whether an antivirus, backup, or synchronization process is holding newly created files.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsWhen the error names unlink or an old screenshot
During cypress run, Cypress clears the contents of screenshotsFolder by default because trashAssetsBeforeRuns defaults to true (screenshots and videos guide). Cleanup can include nested directories, not just image files. If the named path is an old asset, inspect which process owns or locks it and whether the Cypress account can delete it.
You can opt out of automatic deletion:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
screenshotsFolder: 'artifacts/cypress-screenshots',
trashAssetsBeforeRuns: false
})
This preserves existing files and makes cleanup your responsibility; it does not change the destination and does not fix permissions. Keep only disposable artifacts in a folder that Cypress manages, or implement an explicit cleanup step with the correct account.
Windows-specific lock investigation
A Cypress issue reports an intermittent Windows 11 case in which deletion of nested screenshot folders failed; in that reporter’s reproduction, stopping the development process allowed deletion (issue #29404). Treat this as a scenario to test, not a universal diagnosis. Stop Cypress, the application dev server, file watchers, Explorer previews, and other tools that may hold handles, then retry. If the failure disappears, identify which process was locking the tree before re-enabling services one at a time.
Rank #3
4. Verify the path Cypress actually uses
Do not infer the final path from the configured root alone. Cypress 10 changed generated screenshot path derivation to strip common ancestor paths shared by specs. An issue discussion also reports that output paths can differ according to which specs run (issue #22159). Consequently, run one known spec and inspect the produced tree, then run the relevant spec set and compare it.
- Delete or move unrelated files from a disposable test folder.
- Run one spec that calls
cy.screenshot('path-check'). - Record the complete path and whether Cypress created spec-derived directories.
- Run the same command in CI and compare the resolved workspace and account.
- Use the observed path when diagnosing permissions or locks; do not rely on a path copied from a different Cypress version.
If you are changing the setting dynamically, be cautious. A Cypress configuration issue discussion describes changing screenshotsFolder with Cypress.config() inside a test without changing the actual output location (issue #6407). Configure the folder before the run through the project configuration and verify the result instead.
5. A repeatable troubleshooting decision tree
| What the error names | Most useful check | Appropriate fix |
|---|---|---|
mkdir or directory creation |
Can the Cypress process create every parent and nested directory? | Choose a writable project/workspace path and correct account permissions. |
| Image write or temporary rename | Is the destination writable, present, and free of locks or storage exhaustion? | Change the path or permissions; stop locking processes and check disk space. |
unlink, remove, or old asset |
Is trashAssetsBeforeRuns clearing a tree that another process owns? |
Release locks, fix ownership, or set trashAssetsBeforeRuns: false and manage cleanup. |
| Path differs between runs | Did Cypress version or selected spec set change? | Inspect the actual tree and account for common-ancestor/spec-derived paths. |
6. Common fixes that do not solve the underlying EPERM
- Changing only the root string: this cannot fix a locked file in the old root when startup cleanup fails.
- Turning off cleanup for a write error:
trashAssetsBeforeRuns: falseaffects deletion only; it does not grant write permission. - Changing configuration inside a test: runtime mutation may not alter the output location for the run.
- Giving a broad “run as administrator” workaround: it can hide an account or ownership problem and is unsuitable for CI. Prefer a workspace and permissions that match the Cypress process.
- Putting unrelated documents in the screenshot folder: a future run may delete them when cleanup is enabled.
7. Reliability and CI practices
Use an isolated artifact directory
Keep screenshots under the job workspace, separate from source files and persistent user data. Create the directory in the job’s setup phase using the same user that runs Cypress. Publish it as a CI artifact after the run rather than placing it on a shared network location.
Make cleanup intentional
For ephemeral CI workspaces, the default cleanup is convenient. For debugging or retaining history, disable it and give each run a unique artifact directory, or clean that directory in a controlled pre-step. Never assume a cleanup setting is a permissions fix.
Control concurrency
Two jobs should not share the same screenshot tree if either can delete or overwrite files. Use per-job paths or isolated workspaces. If a local watcher and Cypress run concurrently, stop or reconfigure the watcher when testing a lock-related failure.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #4
Record diagnostic context
Log Cypress and Node versions, the resolved workspace, selected specs, effective screenshot setting, and the full EPERM path. This turns an intermittent CI report into a comparison between successful and failing processes without exposing credentials.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.8. Or skip the browser setup
If your goal is a clean image or PDF of a URL rather than Cypress-specific test artifacts, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response reports the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
See the complete parameter list and authentication details in the ScreenshotNeo documentation. A minimal cURL call is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
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)
And in Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also supports full-page lazy-image capture, CSS-selector element capture, device presets or custom viewports, dark mode, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, clicks and waits, request/resource blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, async jobs with signed webhooks, bulk capture for 100 URLs per call, a usage API, and an OpenAPI specification. Existing integrations can use the parameter names common to other screenshot APIs.
Free tools Windows power users keep installed
One-click scans. No signup required.
Every plan includes every feature: 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 shots, with yearly billing giving two months free. Create a free ScreenshotNeo account to try the 1,000-shot allowance.
9. FAQ
Can I keep Cypress’s default folder and still avoid EPERM?
Yes, if the default cypress/screenshots is writable and no process locks its contents. The setting itself is not required to change; verify the failing operation first.
Why does the same spec produce a different-looking directory in another run?
Spec-derived paths and Cypress version behavior can change the subdirectories beneath the root. Compare the actual tree, version, and selected spec set rather than comparing only the root setting.
Should I delete the screenshot folder manually?
Only when you have confirmed it contains disposable artifacts and you are using the same account that runs Cypress. If another process owns files, stop or reconfigure that process first; otherwise the next run can fail again.
Frequently Asked Questions
Can I keep Cypress’s default folder and still avoid EPERM?
Yes, provided the default cypress/screenshots directory is writable and no process locks its contents. Verify the failing operation before changing the setting.
Why does the same spec produce a different-looking directory in another run?
Spec-derived paths and Cypress version behavior can change subdirectories beneath the configured root. Compare the actual tree, version, and selected spec set.
Should I delete the screenshot folder manually?
Only after confirming it contains disposable artifacts and using the same account as Cypress. Release any process locks first.
The Bottom Line
EPERM is a symptom, not a diagnosis: identify the operation and exact path, configure a writable destination, treat cleanup separately, and verify the path generated by your Cypress version and spec set.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

