Resource library

QA How-To

How to Fix "The Cypress binary is missing" in CI

Fix Cypress binary is missing in CI by checking install scripts, cache paths, versions, Docker images, and permissions, then verify the runner before tests.

19 min read | 3,500 words

TL;DR

Run npm ci --include=dev, inspect npx cypress cache path and npx cypress cache list, then run npx cypress install and npx cypress verify in the same CI environment that executes the tests. Persist the binary cache or reinstall it in each job.

Key Takeaways

  • Check the local Cypress npm package and binary cache separately before changing CI.
  • Run cypress install explicitly when lifecycle scripts are skipped or the cache is empty.
  • Use the same cache path, operating system, architecture, and runtime user for install and test.
  • Key restored binary caches to the lockfile and avoid caching node_modules directly.
  • Verify the binary inside the final Docker image or CI job that executes specs.
  • Treat library, display, and permission failures as distinct from an absent binary.

To fix Cypress binary is missing in CI, start when cypress run or cypress verify fails after a dependency install that appeared successful. The package in node_modules and the downloaded desktop binary are separate; CI often has the first without the second.

The cypress npm package is installed, but the Cypress binary is missing.

The error commonly follows a restored node_modules directory, skipped install scripts, a changed cache path, or a job that runs on a different machine from the install job. Follow the decision table, apply the matching fix, and verify the same environment that runs your tests. For a broader suite setup, see the Cypress test architecture guide.

TL;DR

From the project directory containing the lockfile, run the shortest diagnostic and repair sequence:

npm ci --include=dev
npx cypress cache path
npx cypress cache list
npx cypress install
npx cypress verify

npm ci restores the JavaScript package; cypress install downloads the binary if its expected version is absent; verify checks that it launches. In a configured project, finish with npx cypress run. If npm ci deliberately uses --ignore-scripts, keep that policy and run npx cypress install explicitly afterward. On Linux runners, inspect the path printed by cache path rather than assuming every user has the same home directory.

What the Error Actually Means

Cypress installation has two artifacts. Your package manager places the Cypress npm module and command-line wrapper under the project's dependencies. Cypress's install step separately downloads a platform-specific application into a cache. The wrapper can be present and still have nothing to launch. Cypress documents this split in its CI missing-binary guidance.

The relevant cache is normally outside node_modules. On Linux the default is ~/.cache/Cypress; on macOS it is ~/Library/Caches/Cypress. CYPRESS_CACHE_FOLDER overrides that location. Different users, containers, jobs, or shells can therefore see different binaries even while sharing the same checked-out project. The error's printed expected path is more useful than a remembered default.

First establish which half exists. These commands inspect the local project and the binary cache without changing either:

npm ls cypress --depth=0
npx cypress version --component package
npx cypress cache path
npx cypress cache list

If npm ls says Cypress is absent, restore development dependencies before investigating the cache. If the package exists but cache list does not include its version, the install hook did not populate the cache visible to this process. If the version appears yet verify fails, inspect the actual verify error: missing Linux libraries, permissions, and an unusable executable are different diagnoses. The Cypress CLI reference documents version, cache, install, and verify separately.

Root-Cause Decision Table

Symptom Root cause Fix
npm ls cypress finds no local package Development dependencies were omitted or the wrong package directory was used Install from the correct lockfile with npm ci --include=dev
Package exists, cache list has no matching binary Install scripts were skipped Run npx cypress install after dependency installation
One job downloads, another job fails The Cypress cache was not persisted between jobs Restore the binary cache in the test job or install there
Cache exists under one path, runner expects another CYPRESS_CACHE_FOLDER or home directory changed Use one cache folder consistently across install and run
Cache lists only an older version Lockfile upgrade and stale cache Install the package's required binary version
Download fails during install Proxy, certificate, or network restriction Read install debug logs and configure the approved network path
Container build passes, final container fails Binary is absent from the final image or volume hides it Install and verify in the final runtime image
Cache directory cannot be written Different container or runner UID owns the cache Give the running user a writable cache location

Read the row matching your evidence. Clearing all caches before measuring them removes the evidence and can force every job to download a large binary again.

1. Fix Cypress Binary Is Missing When Install Scripts Were Skipped

A successful npm ci --ignore-scripts installs package files but prevents Cypress's postinstall download. The same can happen when an environment-level npm setting disables scripts. A private mirror or security policy may intentionally require this setting; the repair is to make the binary installation an explicit, visible step after the package installation.

Check the npm setting and the command recorded in CI logs:

npm config get ignore-scripts
npm ls cypress --depth=0
npx cypress cache list

If the first command prints true, or the job uses --ignore-scripts, run the following from the same project directory and under the same user as the test job:

npm ci --include=dev --ignore-scripts
DEBUG=cypress:cli* npx cypress install
npx cypress verify

The explicit install is important even if npm reports all packages up to date. Reinstalling only the npm module does not guarantee that its separate cache was populated. The DEBUG prefix shows the binary download and extraction path in a POSIX shell; put DEBUG in the CI step's environment on other shells. If your team permits install scripts, remove --ignore-scripts and use DEBUG=cypress:cli* npm ci --foreground-scripts to see the hook's output. Do not set CYPRESS_SKIP_VERIFY=true to conceal an absent executable: that option skips a launch check, not a missing download. The advanced installation guide describes this two-step debug flow.

2. Restore the Cypress Development Dependency in the Test Job

Many projects declare Cypress in devDependencies. A build job that runs npm ci --omit=dev, or sets NODE_ENV=production, can complete successfully while excluding the Cypress package. Before repairing any binary, check the module itself. In a monorepo, run these commands inside the workspace whose package.json declares Cypress; a root lockfile alone does not prove the test package was installed in the directory where you invoke the CLI.

pwd
npm config get omit
npm ls cypress --depth=0

For an npm lockfile, make development dependencies available in the test job and inspect the result:

npm ci --include=dev
npm ls cypress --depth=0
npx cypress install
npx cypress verify

--include=dev is explicit, so it remains clear even when the runner has production-oriented npm settings. If your deployment image intentionally omits dev dependencies, put Cypress in a separate test stage with its own install. Do not add Cypress to production dependencies solely to make a production-only install command run tests. If npm ls still fails, inspect the package's devDependencies, its lockfile, and the working directory; a binary-cache command cannot compensate for a missing CLI module. For choosing the correct test project in a larger repository, see building a Cypress framework.

3. Persist the Binary Cache Across CI Jobs

A job boundary is usually a machine or filesystem boundary. Installing Cypress in build and running it in e2e works only when the e2e job gets both its project dependencies and the platform-specific Cypress binary. Restoring node_modules by itself is the classic trap: the wrapper returns, while ~/.cache/Cypress stays on the previous worker. Cypress recommends caching the package manager's download cache and Cypress's binary cache, not node_modules itself.

Put the install and test in one job when possible. Add these commands immediately before the test step to prove what that job can actually see:

npm ci --include=dev
npx cypress cache path
npx cypress cache list
npx cypress install
npx cypress verify
npx cypress run

If jobs must remain separate, configure your CI provider to save and restore the directory printed by npx cypress cache path, with a key that includes operating system, CPU architecture, and lockfile hash. Restore it before cypress install; the command then becomes a cheap guard on a hit and downloads on a miss. Cache ~/.npm separately for npm packages. Never restore a Linux binary cache into macOS or an x64 binary into an arm64 job. If your CI provider cannot cache a directory outside the workspace, use CYPRESS_CACHE_FOLDER in every job and place it under a supported workspace path. Verify the restored cache in the consumer job, not only in the producer. See Cypress CI caching guidance.

4. Keep CYPRESS_CACHE_FOLDER Identical During Install and Run

A custom cache path can make Cypress look in an empty directory while a valid binary sits elsewhere. This commonly appears when a CI step sets CYPRESS_CACHE_FOLDER only for installation, a test command runs under a different user, or a relative path resolves from another working directory. The expected path printed by the error tells you where the failing command looked. Compare it with the installation log and cache path output.

For a POSIX CI runner, make one absolute project-local path available to both steps:

export CYPRESS_CACHE_FOLDER="$PWD/.cache/Cypress"
mkdir -p "$CYPRESS_CACHE_FOLDER"
npm ci --include=dev
npx cypress install
npx cypress cache path
npx cypress cache list
npx cypress verify

In a real pipeline, set the same environment variable at the job or container level, not in a shell that ends before the next step. If you cache this directory, restore it to the identical path before installation. CYPRESS_CACHE_FOLDER names the binary cache, while npm's cache contains downloaded package tarballs; confusing them restores the wrong artifact. A home-directory change from /root during a Docker build to /home/node at runtime has the same effect even without an explicit override.

To verify the diagnosis, repeat npx cypress cache path in the exact step that calls npx cypress run. If the two printed paths differ, fix the environment first. Cypress environment variables explains how test configuration variables differ from installation variables.

5. Install the Binary That Matches the Locked Package

A cache may contain Cypress and still lack the version required by the checked-out lockfile. This often happens after a dependency update when CI restores an older cache under a broad key. The CLI looks for its expected version; an older cached application does not satisfy that requirement. A misleading green cache-restore message only means some files were restored.

Compare the npm package version with the cache listing before deciding what to download:

npx cypress version --component package
npx cypress cache list
npx cypress install
npx cypress version
npx cypress verify

cypress install uses the current project's package version unless an install override was configured. cypress version then shows package and binary versions together. If they differ unexpectedly, inspect CYPRESS_INSTALL_BINARY and CYPRESS_RUN_BINARY; the first changes what is downloaded, and the second points execution at an already unpacked binary. Use those overrides only for a deliberate, documented mirror or custom executable. An arbitrary old binary is not a safe way to satisfy a new lockfile.

Make cache keys respond to the lockfile and keep platform and architecture separated. When a cache entry is genuinely corrupt, delete that CI cache entry or use npx cypress cache clear in a controlled repair, then reinstall. cache clear removes every locally cached Cypress version, so it is a last resort on shared agents. To understand why a narrow spec succeeds after installation, read running one Cypress test.

6. Diagnose a Blocked or Interrupted Download

If cypress install fails before extraction, the missing binary is an outcome, not the initiating fault. Read the first download error rather than only the later cypress run failure. Corporate proxies, TLS inspection, blocked hosts, interrupted transfers, and exhausted disk space produce different installation logs. Cypress's DEBUG=cypress:cli* output shows the URL and local destination that matter for investigation.

DEBUG=cypress:cli* npx cypress install
npx cypress cache list
npx cypress verify

Use your organization's approved proxy and certificate settings when the log identifies a network restriction. For an internal artifact mirror that serves the Cypress download layout, configure its documented URL in the installation environment, then rerun the same commands:

export CYPRESS_DOWNLOAD_MIRROR="https://your-approved-cypress-mirror.example"
DEBUG=cypress:cli* npx cypress install
npx cypress verify

Replace the example domain with a real mirror your organization operates. A mirror must actually provide the requested platform and package version; setting the variable cannot create a missing artifact. If your mirror uses another URL layout, use Cypress's documented CYPRESS_DOWNLOAD_PATH_TEMPLATE rather than guessing an endpoint. For an offline runner, download the correct binary through an approved connected stage and use the documented CYPRESS_INSTALL_BINARY file or URL option at install time. Avoid treating CYPRESS_RUN_BINARY as a download URL: it expects an unpacked executable path. The advanced installation reference details the proxy, mirror, and custom binary settings.

7. Fix Cypress Binary Is Missing in Docker

A container's filesystem matters more than the host's cache. Installing Cypress on a laptop, mounting only the repository into a container, and invoking the container's CLI does not transfer the host's application binary. Multi-stage Docker builds create another gap when installation occurs in a builder stage but the final stage copies only node_modules. A mounted volume can also hide a cache directory created during image build.

With the official cypress/base image, which includes operating-system dependencies but not the Cypress package, install in the final test image. Replace the tag placeholder with an available Node image tag compatible with your project's locked Cypress version:

FROM cypress/base:<your-node-version>
WORKDIR /e2e
COPY package.json package-lock.json ./
RUN npm ci --include=dev && npx cypress verify
COPY . .
CMD ["npx", "cypress", "run"]

Keep host node_modules out of the build context with .dockerignore; copying it over the installed container dependencies can overwrite the image's correct package files. Build and run from the project root after replacing the placeholder:

docker build -t local-cypress-e2e .
docker run --rm local-cypress-e2e

For a project that intentionally uses the preinstalled image, cypress/included:<your-cypress-version> already contains Cypress. Match the image's Cypress version to the version expected by your project, or remove the redundant project package as the image documentation recommends for that pattern. Its default entrypoint runs cypress run:

docker run --rm -v "$PWD:/e2e" -w /e2e cypress/included:<your-cypress-version>

Use the exact version tag available for your chosen image rather than latest. The official Docker image guide explains the included image's entrypoint and version matching.

8. Repair Cache Permissions Under the Runtime User

A binary can exist but be inaccessible to the user running CI. In containers, an image may install as root, then run tests as node; a mounted cache may be owned by another UID. The resulting log might show EACCES while reading or writing cache state. Do not diagnose this as a network issue simply because reinstalling as a privileged user succeeds.

Inspect the actual identity and cache ownership in the failing container or job:

id
npx cypress cache path
ls -ld "$(npx cypress cache path)"
npx cypress verify

Choose a cache location writable by the runtime user, then install and verify as that user. In a POSIX shell, a local directory owned by the current user makes the relationship explicit:

export CYPRESS_CACHE_FOLDER="$PWD/.cache/Cypress"
mkdir -p "$CYPRESS_CACHE_FOLDER"
DEBUG=cypress:cli* npx cypress install
npx cypress verify

If a CI cache restore creates root-owned files, fix ownership in the image or cache restore process according to your runner's user model. Avoid broad world-writable permissions as a shortcut. On GitHub Actions container jobs, the Cypress Docker image repository documents the --user 1001 container option for avoiding permission issues, but the correct UID still depends on your job. If permission changes expose a new library or display error, solve that new failure on its own terms; it means the binary is now being found.

9. Separate Missing Binary From Linux System Dependencies

A failed cypress verify is useful evidence, but it does not always mean the binary is missing. Once cache list includes the matching version, Cypress may fail to launch because the Linux image lacks shared libraries or an X server. Installing again will not add those OS dependencies. The official cypress/base and cypress/browsers images include the operating-system pieces expected by Cypress; a generic minimal Node image may need extra packages.

npx cypress cache list
npx cypress version
npx cypress verify
npx cypress info

Read the first specific loader or display error from verify. If the executable is found but a library is absent, use Cypress's current system requirements for your distribution or switch to an official Cypress base image. If parallel Linux jobs encounter Xvfb startup failures, follow the Cypress CI Xvfb guidance; that is a display issue after binary discovery. npx cypress info reports the detected operating system, browsers, and cache location, giving reviewers enough context to reproduce the runner. The verification command for this branch is still npx cypress verify, followed by npx cypress run only after the executable launches.

How to Verify the Fix

Verify in the same job, container, user, working directory, and environment that produced the failure. A green install step in another job proves too little. Use this sequence as a temporary CI diagnostic step after dependencies are available:

npm ls cypress --depth=0
npx cypress version --component package
npx cypress cache path
npx cypress cache list
npx cypress verify
npx cypress run

npm ls must show the project package. cache list must include its version in the path that cache path prints. verify must finish successfully. Finally, run must start the test runner and discover your project's specs. A test assertion failure at that point is a separate application or test issue, not the missing-binary installation error. If you want a focused smoke pass after repair, use the documented --spec option with a real path from your repository, then return to the normal CI test command.

Capture the output of cache path, cache list, version, and verify in the failing job's logs before removing temporary diagnostics. They pinpoint whether a recurrence came from a new package version, a cache miss, or a changed runtime user. If tests start but behave intermittently, use the Cypress flaky-test troubleshooting guide rather than reworking installation again.

Prevent It From Coming Back

Keep dependency installation and binary verification adjacent to test execution. A straightforward npm job runs npm ci --include=dev, npx cypress verify, then npx cypress run under one runtime identity. Add an explicit npx cypress install guard if your package manager skips lifecycle scripts or if cache restoration is unreliable. That command is easier to audit than hoping a postinstall hook ran in an earlier stage.

Cache by the lockfile plus OS and architecture. Cache the binary directory printed by npx cypress cache path; cache npm's package download directory separately. Avoid saving node_modules, since that can carry a package wrapper across jobs without causing the binary installation hook to run. Document any CYPRESS_CACHE_FOLDER, CYPRESS_INSTALL_BINARY, or CYPRESS_RUN_BINARY setting near the pipeline definition, including which stage produces the artifact and which user consumes it.

For Docker, pin a real available image tag that matches your project's selected version and verify inside the final image. For GitHub Actions using the official Cypress action, let the action manage its documented cache unless you have a concrete reason to replace that behavior. Running parallel Cypress jobs adds cache and platform boundaries; the Cypress parallelization guide covers suite distribution once every worker can launch the binary. Revisit cache keys whenever the lockfile or runner image changes.

Interview Questions and Answers

Q: Why can npm ci succeed while Cypress says its binary is missing?

The npm package contains the CLI wrapper, while the desktop application is downloaded separately during installation. A skipped postinstall hook, failed download, or absent cache leaves the wrapper installed. I inspect npm ls cypress and npx cypress cache list to distinguish those artifacts.

Q: What is your first diagnostic in a failing CI job?

I print npx cypress cache path, npx cypress cache list, and npx cypress version --component package in that job. The paths and versions tell me whether the runner is looking in the wrong place or has the wrong binary version. I also read the expected path in the original error.

Q: When is npx cypress install better than another npm ci?

When the package already exists and the cache lacks its binary, cypress install targets the missing artifact directly. It is especially useful after --ignore-scripts or a cache miss. I follow it with npx cypress verify to prove launchability.

Q: Why should a cache key include platform and architecture?

The Cypress binary is platform-specific. Sharing a restored archive between incompatible runners can produce a cache hit containing an unusable executable. The lockfile belongs in the key so a package upgrade does not silently reuse only an older binary.

Q: What changes when tests run in Docker?

I verify the binary in the final test image, under its runtime user. A builder-stage install or a host cache does not automatically appear in the container that runs tests. I also check whether a bind mount hides a cache directory created during build.

Q: How do you distinguish a missing binary from missing Linux libraries?

I look at cache list and the exact verify error. If the required binary is present but the loader reports a missing library, I fix the image's OS dependencies. Re-downloading the same executable cannot supply system libraries.

For deeper practice with CI diagnosis, work through CI/CD troubleshooting interview questions for QA.

Common Mistakes

  • Caching node_modules and assuming that also preserves Cypress's application cache.
  • Running npx cypress verify in the install job but never in the job that executes specs.
  • Comparing only cache-hit status without checking the package and binary versions.
  • Setting CYPRESS_CACHE_FOLDER for one shell step and losing it in the next step.
  • Clearing the entire binary cache before recording the expected and actual paths.
  • Treating CYPRESS_RUN_BINARY as a download location; it must point to an unpacked executable.
  • Using CYPRESS_SKIP_VERIFY=true to hide a real installation problem.
  • Copying host node_modules into a Docker image after the image installed its own dependencies.
  • Fixing a later Xvfb or shared-library failure with repeated binary downloads.
  • Pinning a Docker image to an unrelated Cypress version or a floating latest tag.

Conclusion

To fix Cypress binary is missing, confirm that the package and its platform-specific application are both available to the exact CI process that runs the tests. Start with the cache path and list, install the missing version, and require cypress verify before the suite starts. Then make the cache boundary explicit in the pipeline so the next clean runner follows the same successful path.

Interview Questions and Answers

Explain why the Cypress npm package can be present without the Cypress binary.

The package manager installs the CLI wrapper into the project, and Cypress separately downloads a platform-specific desktop application into a cache. If a lifecycle script is skipped, the download fails, or CI restores only node_modules, the wrapper still exists. I compare npm ls cypress with cypress cache list before acting.

How would you debug the missing-binary error on a fresh CI runner?

I first record the expected path in the error. In the failing job I run cypress version --component package, cypress cache path, and cypress cache list. I then check lifecycle-script settings, the download log, and whether the runner restored the cache before tests.

When would you add an explicit cypress install step?

I add it when scripts are intentionally ignored, cache restoration is unreliable, or separate jobs have distinct filesystems. It targets the binary that the local package requires. I keep cypress verify immediately afterward so an incomplete download fails before specs start.

How would you design a safe Cypress cache key?

I include the OS, architecture, and dependency lockfile hash. Cypress's application is platform-specific and its expected version changes with the package lock. I cache the binary directory separately from the npm package cache and verify in the consuming job.

What is the difference between CYPRESS_CACHE_FOLDER and CYPRESS_RUN_BINARY?

CYPRESS_CACHE_FOLDER changes where Cypress stores and finds downloaded application versions. CYPRESS_RUN_BINARY instead points the CLI directly at an already unpacked executable. I use the former for CI cache persistence and the latter only for a deliberate custom binary setup.

How do you make a Docker-based Cypress run reliable?

I choose a compatible official Cypress image or install the locked project package in the final test image. I keep host node_modules out of the image context, run verify inside the final container, and check the runtime user's cache path. A builder-stage success does not prove the final image can launch Cypress.

How do you tell a missing binary from an OS dependency failure?

I use cypress cache list and inspect the first cypress verify error. An empty cache or missing required version points to installation. An existing executable that reports a missing shared library or display problem points to the container's system environment.

Frequently Asked Questions

Why does CI say the Cypress binary is missing after npm ci?

The Cypress npm package and platform-specific application are installed separately. The postinstall download may have been skipped or failed, or the application cache may not exist in the test job. Inspect the package with npm ls cypress and the cache with npx cypress cache list.

What command downloads a missing Cypress binary?

Run npx cypress install from the project that has Cypress installed. It installs the binary expected by that local package unless an install override is configured. Follow it with npx cypress verify.

Where is the Cypress binary cache in Linux CI?

The default Linux location is ~/.cache/Cypress for the user running the command. Run npx cypress cache path in the failing job to see the effective location, especially when CYPRESS_CACHE_FOLDER or the runtime user differs.

Should I cache node_modules to fix the missing Cypress binary?

No. node_modules contains the Cypress wrapper, while the application binary normally lives in a separate cache. Cache the directory reported by cypress cache path and your package manager's download cache, and install dependencies cleanly in each job.

Can I use Cypress with npm ci --ignore-scripts?

Yes, but the skipped postinstall hook will not download Cypress. Run npx cypress install explicitly after npm ci --ignore-scripts, then run npx cypress verify in the same job.

Why does Cypress work in Docker build but fail when the container runs?

The final image may lack the builder stage's binary, a bind mount may hide the cache, or the runtime user may resolve a different home directory. Inspect cache path and verify inside the final running container under its actual user.

Does CYPRESS_SKIP_VERIFY fix a missing binary?

No. It skips Cypress's verification step; it does not download an application or correct the cache path. Install the matching binary and verify it before running tests.

Related Guides