QA How-To
How to Fix Cypress "cy.visit() failed trying to load"
Fix Cypress cy visit failed trying to load by diagnosing server startup, URLs, HTTP responses, redirects, page loads, proxies, CI timing, and Docker networking.
18 min read | 3,517 words
TL;DR
Probe the exact failing URL from the Cypress runner. Start or repair the server for connection errors, fix the route for HTTP errors, inspect pending resources for load timeouts, and wait for readiness before running tests in CI.
Key Takeaways
- Read the network or HTTP detail under the cy.visit() error before changing test settings.
- Probe the exact URL from the same machine or container that runs Cypress.
- Align the server port, Cypress baseUrl, and CI readiness check.
- Treat 404 and 500 responses as application or route failures, not connection failures.
- Use failOnStatusCode: false only for intentional negative tests.
- Investigate stuck page resources before increasing the visit timeout.
- Use a container-reachable host or service name when Cypress runs in Docker.
To fix Cypress cy visit failed trying to load, read the line immediately below the URL before changing test code. The error appears when cy.visit() cannot fetch a usable HTML page or cannot finish loading it. A refused connection, an HTTP error, and a missing load event require different fixes.
CypressError: cy.visit() failed trying to load:
http://127.0.0.1:5173/
We attempted to make an http request to this URL but the request failed without a response.
We received this error at the network level:
> Error: connect ECONNREFUSED 127.0.0.1:5173
This is one real form of the message; your host, port, and network error will differ. A response such as 404: Not Found appears under the same opening line, but proves the server did answer. Keep the full output, including redirect and content type details, while you diagnose it. Cypress documents the visit command and its response, content type, and page load requirements.
TL;DR
Run this from the environment that launches Cypress, replacing the URL with the one printed in your failure:
curl -v --max-time 10 http://127.0.0.1:5173/
npx cypress run --config baseUrl=http://127.0.0.1:5173
If curl cannot connect, start the app and check its listening address. If it returns 404, 500, or a redirect to the wrong place, repair the route or authentication flow. If it returns HTML promptly but Cypress waits for a page load, inspect the page's subresources and only then consider pageLoadTimeout. In CI, start the app and wait for it before Cypress runs. In Docker, run curl inside the Cypress container because its localhost may refer to a different machine. The related Cypress framework setup guide covers a failure that can happen before a spec starts.
What the Error Actually Means
cy.visit() requests a URL, follows redirects, expects a successful HTML response, and waits for the browser's load event. Cypress prefixes a relative path such as cy.visit('/') with e2e.baseUrl. Its default visit behavior fails on response codes outside the successful range, while failOnStatusCode: false changes only that status check. The options retryOnStatusCodeFailure and retryOnNetworkFailure cover transient failures, but retries do not repair a consistently wrong address.
Find where the command fails. If Cypress says it could not make a request and shows ECONNREFUSED, there was no usable listener at that destination. ENOTFOUND points toward name resolution. If it reports 404 or 500, a server responded and the route or server behavior is now the lead. If the first HTML response succeeds and Cypress reports that the page did not fire its load event, investigate resources and browser behavior. A later cy.get() timeout is a separate failure, even when it follows a successful visit. Cypress's error messages reference separates page load errors from command timeouts.
Record four values before editing anything: the exact URL after Cypress applies baseUrl, the network or HTTP detail, the location running Cypress, and the response seen by curl at that location. Those observations decide which root cause below applies. A screenshot of a page open on your laptop is not proof that a CI runner or container can reach the same address.
Root-Cause Decision Table
| Symptom in Cypress or a probe | Likely root cause | First fix to try |
|---|---|---|
ECONNREFUSED and curl cannot connect |
App is stopped, crashed, or listening elsewhere | Start the app; match the host and port |
| URL in failure has the wrong host, port, or path | baseUrl or visit path is wrong |
Print effective config; correct the URL |
404: Not Found |
Route or SPA fallback is missing | Fix the path or server fallback |
500: Server Error |
Application or upstream dependency failed | Read application logs and repair the server |
| Redirect lands on login or an unexpected host | Authentication or redirect configuration | Establish session or correct redirect target |
ENOTFOUND, proxy error, or certificate failure |
DNS, proxy, or TLS trust | Probe from the runner and configure the network |
HTML returns, but load does not fire |
Slow or stuck page resource | Identify the resource; adjust timeout only if needed |
| Works on host, fails in Docker | localhost names the wrong network namespace |
Use the service name or supported host address |
The table is a triage map, not a list of options to enable all at once. In particular, suppressing status failures can turn a real error page into a false green test. For general Cypress debugging techniques, keep the command log and browser Network panel alongside server logs.
1. Fix Cypress cy visit failed trying to load when the app is not listening
A refused connection is the most direct case: the address exists, but nothing accepts the TCP connection there. For a Vite app whose package.json has a dev script, start the server in one terminal with an explicit port. --strictPort matters because Vite otherwise may move to another free port while Cypress still visits the old one.
npm run dev -- --host 127.0.0.1 --port 5173 --strictPort
In another terminal on the same machine, probe the root page and then run a focused spec. Replace the spec path with an existing file in your project.
curl -v --max-time 10 http://127.0.0.1:5173/
npx cypress run --config baseUrl=http://127.0.0.1:5173 --spec cypress/e2e/home.cy.js
Verify that curl prints an HTTP status and HTML before relying on the test result. If npm run dev exits, read its terminal output for a compile error, missing environment variable, or occupied port. If your app uses another framework, run its normal start script instead and keep the actual address consistent with Cypress. A failed port probe is a server startup problem, not a reason to increase Cypress's defaultCommandTimeout.
For a repeatable smoke test, create cypress/e2e/home.cy.js with a minimal visit and a visible page assertion:
describe('home page', () => {
it('loads the application shell', () => {
cy.visit('/')
cy.location('pathname').should('eq', '/')
cy.get('body').should('be.visible')
})
})
That test proves the document loads; add an app-specific landmark assertion once you know the expected UI. If curl works only through a browser profile's proxy or VPN, investigate that network difference in section 6. Cypress best practices recommend starting the web server outside a test, not from cy.task().
2. Fix Cypress cy visit failed trying to load when the URL is wrong
A running server can still fail if Cypress visits a stale port, a misspelled host, or an unintended route. Check the URL printed in the error, not merely the value you remember putting in a config file. Cypress can receive configuration from a file, a command-line override, or an environment variable. A relative cy.visit('/reports') uses baseUrl; a full URL bypasses it.
For the Vite example, use one address in cypress.config.ts and let specs visit relative paths:
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: 'http://127.0.0.1:5173',
},
})
it('opens the home route', () => {
cy.visit('/')
cy.location('origin').should('eq', 'http://127.0.0.1:5173')
})
Run npx cypress run --spec cypress/e2e/home.cy.js after placing the first test in that file, or run the complete suite if the small spec does not exist. Inspect any CI command for --config baseUrl=... and any runner environment for CYPRESS_BASE_URL; those may override the file. In the same shell, printf '%s\n' "$CYPRESS_BASE_URL" reveals an environment override without exposing other variables. The Cypress environment variables guide explains when to use configuration versus test data.
Check path joining too. baseUrl: 'http://127.0.0.1:5173/app/' and cy.visit('reports') target a different route from a root-based application. Prefer a root base URL and explicit paths when the app uses normal pathname routing. If curl -I shows the expected host but curl -L lands elsewhere, read the redirect chain before changing Cypress. A successful curl to / does not prove that /reports exists.
3. Repair an HTTP error or non-HTML response
An HTTP status is evidence that the network path worked. 404 usually means the requested path does not exist or the server lacks a single-page application fallback. 500 means the application or an upstream dependency failed. A JSON endpoint is also the wrong target for cy.visit() because the command expects HTML. Do not use an API health URL as the visit target.
Probe the exact route with headers and body, following redirects when appropriate:
curl -i --max-time 10 http://127.0.0.1:5173/reports
curl -iL --max-time 10 http://127.0.0.1:5173/reports
Look for the final status and a Content-Type containing text/html. On a Vite development server, a client-side route usually receives the app shell; on a production static host, direct deep links need a rewrite to the app's index.html. If the route belongs to a backend-rendered app, fix its router instead. Verify the repair by re-running both curl -iL and npx cypress run --spec cypress/e2e/home.cy.js with a test that visits the repaired route.
If you deliberately test an error page, turn off visit's status check locally and assert the status separately. That does not make an error page valid application content:
it('shows the not-found page', () => {
cy.visit('/missing-page', { failOnStatusCode: false })
cy.request({ url: '/missing-page', failOnStatusCode: false })
.its('status')
.should('eq', 404)
})
Use this pattern only when a real route returns the intended 404 page. If your SPA serves its app shell with status 200 for unknown routes, test the app's not-found UI instead of asserting 404. Avoid setting failOnStatusCode: false globally as a reaction to a broken homepage. Cypress's cy.visit options make clear that this setting alters failure behavior, not the server's response.
4. Correct authentication and redirect targets
A visit may start at the right URL and finish at an identity provider, login page, or wrong environment. Redirects are followed automatically, so inspect the final destination with curl -iL and with cy.location('href') when the visit succeeds. If the redirected request returns an error, the top-level message may still name the original visit. Match the redirect's host and protocol to the environment under test.
For a protected route that should send unauthenticated users to /login, test that contract directly:
it('sends an anonymous visitor to login', () => {
cy.visit('/reports')
cy.location('pathname').should('eq', '/login')
})
If the intended test is for an authenticated page, establish the session through your real login flow or a documented test login API before visiting /reports. Do not paste a made-up token into local storage: an app may need an HTTP-only cookie, CSRF state, or server session. The Cypress session guide covers caching a known-good login flow after it works once.
To verify a redirect repair, run curl -iL for the starting route and assert the final cy.location('pathname') in a focused test. A redirect from HTTP to HTTPS is fine if the HTTPS endpoint has a valid certificate and the destination is reachable from the runner. A redirect from an internal service name to localhost often breaks container tests; repair the application's public URL setting instead of forcing Cypress to visit a random host. If the browser reaches a different superdomain and you then need to interact with that page, use cy.origin() for those commands. The cross-domain Cypress guide explains that case; cy.origin() does not make a dead URL load.
5. Diagnose a page that never fires load
A response can be successful while cy.visit() still waits. The page's load event depends on document resources such as scripts, stylesheets, and images. A hanging resource, a proxy that never completes a response, or an application redirect loop can leave the command unresolved. Cypress's pageLoadTimeout is the relevant limit; defaultCommandTimeout controls other commands and cannot repair this wait.
First inspect the browser Network panel or server access logs for a request that stays pending. Compare a direct visit in the same browser and environment used by Cypress. Then isolate the page with a single spec:
it('loads the reports document', () => {
cy.visit('/reports', { timeout: 60000 })
cy.location('pathname').should('eq', '/reports')
})
Run npx cypress run --spec cypress/e2e/home.cy.js after putting the test in that file. The timeout option is useful when a known, bounded resource is legitimately slow; it is not proof that the resource is healthy. The value above is an illustrative per-visit setting, not a required Cypress version default. If a third-party image or analytics script is stuck, fix the page's loading behavior or serve a deterministic test resource. If the page redirects forever, correct its authentication or routing logic instead of adding time.
Do not confuse a successful visit followed by cy.get('[data-cy=report]').should('exist') timing out with a visit failure. The former points at rendering, selectors, or an application API. The guide to waiting for a Cypress API response is useful once the document itself has loaded. cy.intercept() is for application requests and should be registered before cy.visit() if the app fires them during startup; it cannot replace the need for a valid initial HTML document.
6. Check DNS, proxy, and TLS from the runner
ENOTFOUND means the hostname could not be resolved where Cypress ran. A proxy connection error implicates the configured upstream proxy. A certificate failure belongs to HTTPS trust or the server's TLS setup. These are different from an application returning 404. Corporate laptops often have browser-managed network settings that are absent in a terminal or CI worker.
Probe both name resolution and the TLS handshake from the Cypress execution environment:
getent hosts app.internal.example || true
curl -v --max-time 10 https://app.internal.example/
env | grep -iE '^(http_proxy|https_proxy|no_proxy)=' || true
getent is available on many Linux runners; on macOS, use dscacheutil -q host -a name app.internal.example instead. Replace the example hostname with your real one. A curl certificate error warrants fixing the CA chain or the runner trust store. Do not disable TLS validation as a blanket test setting. If the app requires a corporate proxy, configure the system HTTP_PROXY or HTTPS_PROXY used by the runner and set NO_PROXY for internal hosts that must bypass it. Cypress's proxy configuration reference documents its precedence and the automatic bypass for loopback addresses.
After the network change, repeat curl -v and then npx cypress run --spec cypress/e2e/home.cy.js. If only Cypress fails, compare the proxy environment inherited by the Cypress process with the shell used for the probe. If only one browser fails, compare that browser's certificate and proxy setup. Avoid assuming a browser tab on a developer workstation proves reachability from a remote agent. For network stubbing after the initial page loads, see Cypress network stubbing; a stub does not resolve DNS for the first visit.
7. Use an address reachable from Docker
Inside a container, 127.0.0.1 and localhost point to that container. A Vite server on the Docker host is a separate network endpoint. On Docker Desktop, bind the app to 0.0.0.0 and use host.docker.internal from the Cypress container. Choose an official Cypress image tag that matches the Cypress version installed by your project; do not assume a floating tag has the same binary and package versions.
Start the Vite app on the host:
npm run dev -- --host 0.0.0.0 --port 5173 --strictPort
Then set CYPRESS_IMAGE_TAG to an official cypress/included tag matching your installed Cypress version and run this from the project root on Docker Desktop:
export CYPRESS_IMAGE_TAG='<tag-matching-your-installed-cypress-version>'
docker run --rm \
-v "$PWD":/e2e -w /e2e \
--entrypoint sh \
"cypress/included:$CYPRESS_IMAGE_TAG" \
-lc 'curl -fsS http://host.docker.internal:5173/ >/dev/null && npm ci && cypress run --config baseUrl=http://host.docker.internal:5173'
The curl inside the container is the verification gate. If the image lacks curl, run a Node HTTP probe or use an image with it; do not infer connectivity from host-side curl. On Linux, --network host can let the container reach a host-bound local server, but that mode has different behavior from Docker Desktop. In Docker Compose, address another service by its service name and container port, such as http://app:5173, after configuring that app to listen on 0.0.0.0. depends_on alone does not prove the HTTP route is ready; add a readiness check. The official Cypress Docker images repository lists image variants and tags.
CI and Docker Variants
For CI on a single runner, install dependencies, start the app, wait for the exact URL, and run Cypress. Cypress's CI guide recommends start-server-and-test for this lifecycle. Add it as a development dependency and define scripts in your project's package.json:
npm install --save-dev start-server-and-test
{
"scripts": {
"dev:test": "vite --host 127.0.0.1 --port 5173 --strictPort",
"cy:run": "cypress run",
"test:e2e": "start-server-and-test dev:test http://127.0.0.1:5173 cy:run"
}
}
Merge those scripts into the existing package.json; do not replace its other fields. The verification command is npm run test:e2e. The helper waits for an HTTP response before launching cy:run and stops the server afterward. For an app whose root path intentionally returns a non-200 status, choose a stable readiness URL that responds with 200. For a server that does not accept HEAD, Cypress documents the http-get:// form for the wait target. A health check should prove the app is actually ready, not just that a TCP port opened.
In a CI job that already starts the app, use the same sequence rather than starting a second instance on the same port. Print the app logs when the readiness step fails. If CI executes Cypress in Docker while the app runs in another Compose service, the app's service name belongs in baseUrl, not the host's loopback address. Set that value via --config baseUrl=http://app:5173 for the container run and verify it with a container-side HTTP probe. Keep the URL that Cypress prints in the failed command as the source of truth for diagnosis.
How to Verify the Fix
Use a three-part check. First, from the same environment that runs Cypress, request the exact path and inspect the final status and content type:
curl -iL --max-time 10 http://127.0.0.1:5173/
Second, run one focused spec that executes cy.visit('/') and asserts a stable page element. The home.cy.js example above is a starting point, but an app-specific heading or [data-cy] landmark is stronger than a visible body. Third, run the entire suite so a fix for one route does not hide failures on protected or deep-link routes:
npx cypress run --config baseUrl=http://127.0.0.1:5173 --spec cypress/e2e/home.cy.js
npx cypress run --config baseUrl=http://127.0.0.1:5173
Success means the first page loads, the expected route or landmark appears, and the same checks pass in CI or Docker if that was the failing environment. Do not count a run as fixed because failOnStatusCode: false lets it continue to an error page. Capture the original failing URL and its new curl result in the pull request so another engineer can reproduce the change. If the error moves from cy.visit() to a DOM assertion, the connectivity problem is solved and the remaining failure should be debugged separately.
Prevent It From Coming Back
Keep the app start command, Cypress baseUrl, and CI readiness URL aligned in one script or documented environment setting. Pin the port for test runs so a development server cannot silently shift to a different one. Add a direct-link smoke test for an important nested route, because a successful homepage does not verify SPA fallback rules on a static host. If your app uses redirects, assert the final path and host so an environment variable cannot accidentally send tests to production or an unavailable identity service.
Run a container-side readiness probe whenever the test runner moves into a different network namespace. Keep official Cypress image tags matched to the installed package, and update them together when upgrading. For flaky third-party resources, measure which request delays load before raising timeouts. Use Cypress flaky test guidance to separate intermittent infrastructure failures from assertions that depend on unstable UI state. A brief note in CI logs showing the resolved baseUrl, server startup output, and HTTP readiness result usually saves more time than a large retry count.
Interview Questions and Answers
Q: What is the first diagnostic action after cy.visit() fails? Read the full error and probe the exact URL from the Cypress runner. The network-level detail determines whether to inspect the listener, HTTP route, DNS, TLS, or page load.
Q: Why can a page load in a browser but fail in Docker? The browser and container may use different network namespaces. localhost inside the container reaches the container, not the host app; use an address reachable from the container and prove it with a probe there.
Q: When is failOnStatusCode: false appropriate? Use it for an intentional negative test of an HTTP error page and assert the expected status or UI. It is not a fix for an app route unexpectedly returning 404 or 500.
Q: Does cy.origin() fix a failed initial visit? No. It scopes later commands to another origin after navigation succeeds. A dead URL, wrong redirect destination, or TLS failure must be fixed at the network or app layer.
Q: Which timeout applies when a page never fires load? pageLoadTimeout, or the visit's timeout option, controls that wait. Investigate stalled resources first because a longer limit only masks a stuck page.
Q: How do you prevent a CI startup race? Start the web server, wait for a meaningful HTTP readiness response, then run Cypress. start-server-and-test implements that sequence and shuts down the server after tests.
The interviewQnA field below provides fuller model answers for these questions, including the evidence an interviewer should expect. The Cypress interview questions guide covers broader command and architecture topics.
Common Mistakes
- Increasing
defaultCommandTimeoutfor a connection refusal. The visit cannot reach a server, so DOM command timing is irrelevant. - Assuming
localhostis shared by the host, CI worker, and Docker container. Run the same probe where Cypress actually executes. - Disabling status failures globally. A
404login redirect or500homepage can then look like a passing visit. - Starting Cypress immediately after backgrounding a server. Wait for an HTTP response that indicates readiness.
- Testing an API JSON endpoint with
cy.visit(). Usecy.request()for an API and reservecy.visit()for HTML pages. - Raising
pageLoadTimeoutwithout finding a pending resource. The run becomes slower while the root cause remains. - Checking only
/when the failing test visits a deep route. Probe the exact final URL and its redirect chain. - Adding
cy.origin()for an initial network failure. Origin handling applies after the destination loads.
Conclusion
The phrase "cy.visit() failed trying to load" identifies the failed command, but the useful diagnosis is in the response or network line below it. Start by probing the exact URL from Cypress's environment. Repair a missing listener, wrong URL, HTTP response, redirect, network path, or stalled load according to that evidence, then verify the focused spec and full suite in the same environment that originally failed.
Interview Questions and Answers
How would you triage a Cypress cy.visit() failed trying to load report?
I would capture the exact URL and the detail under the top-level error, then request that URL from the Cypress runner. ECONNREFUSED leads to server and port checks; an HTTP status leads to route and app logs; a load timeout leads to pending resources. I would reproduce with one spec before changing configuration.
What is the difference between a visit 404 and ECONNREFUSED?
A 404 means an HTTP server responded, so the route, rewrite, or redirect is the primary suspect. ECONNREFUSED means the connection was rejected before any HTTP response existed. The fixes belong to different layers and should not be combined into a generic retry.
Why should a Cypress project configure baseUrl?
A baseUrl lets tests use relative paths and centralizes the application address. It also makes local and CI targets easier to switch through configuration. I still inspect the resolved URL in failures because command-line or environment overrides can change the effective value.
When is failOnStatusCode: false a valid visit option?
It is valid in a negative test where the expected document intentionally returns an error status. I would assert the response or visible error state so the test cannot pass on an unrelated failure. I would not apply it broadly to hide unexpected 404 or 500 responses.
How do you eliminate a Cypress CI server startup race?
I start the app in the job, wait for a meaningful HTTP readiness URL, and only then launch Cypress. start-server-and-test is one supported way to manage that lifecycle. If readiness fails, I preserve server logs rather than spending time on browser assertions.
Why does localhost often fail when Cypress runs in Docker?
Loopback is scoped to the container, while the application may run on the host or in another container. I probe from inside the Cypress container and use the Compose service name or a supported host address. I also ensure the app listens on an interface reachable from the container.
How do you distinguish a page load timeout from an element assertion timeout?
A page load timeout occurs while cy.visit() waits for the document load event. An element assertion timeout occurs after visit succeeds and Cypress searches for UI state. I inspect browser network requests for the first case and application rendering or selectors for the second.
Frequently Asked Questions
Why does cy.visit() fail when the page opens in my browser?
Your browser and Cypress may not share the same network, proxy, certificate store, or URL. Run curl against the exact failing URL from the Cypress runner, especially when Cypress runs in CI or Docker. Compare the final redirect and response, not only the starting address.
What does ECONNREFUSED mean in a Cypress visit error?
The requested host was reached but no process accepted the connection on that port. Start the app, check whether it crashed, and confirm the listening address and port. A Cypress assertion timeout setting cannot fix a refused connection.
Should I set failOnStatusCode to false for a 404?
Only if the test intentionally visits a not-found page and asserts its behavior. An unexpected 404 means the route, redirect, or static-host fallback needs repair. Suppressing the status check can hide the actual regression.
Which Cypress timeout controls a page that does not finish loading?
The pageLoadTimeout configuration, or the timeout option on cy.visit(), controls how long Cypress waits for the load event. Inspect pending document resources and redirect loops before increasing it. defaultCommandTimeout applies to other commands.
How do I fix cy.visit() in a Docker container?
Probe the destination from inside the Cypress container. Its localhost is its own network namespace, so address a Compose app by service name or use host.docker.internal on Docker Desktop for a host app. Bind the app so it is reachable from that network.
Can cy.origin() repair a failed cy.visit() request?
No. cy.origin() lets later Cypress commands interact with a page on another origin after navigation works. It does not start a server, repair DNS, accept a certificate, or turn an HTTP error into a successful page.
Why does cy.visit() fail only in CI?
Common causes are a server startup race, a missing build or environment variable, or a URL that points to the wrong runner. Start the application, wait for an HTTP readiness response, and probe the URL from the CI job before Cypress starts.
Related Guides
- How to Fix "Cypress failed to start" and cypress verify Errors
- How to Fix Appium WebDriverAgent Failed to Start on iOS
- How to Fix "Cypress could not verify that this server is running"
- How to Fix "Playwright Test did not expect test() to be called here"
- How to Fix "The Cypress binary is missing" in CI
- How to Fix cy.intercept Not Working in Cypress