QA How-To
How to Fix Cypress "Uncaught exception originated from your application code"
Fix Cypress uncaught exception from application code by tracing real app errors, repairing request and state failures, and verifying narrow exception handling.
17 min read | 3,104 words
TL;DR
Find the first application stack frame, then fix the render, promise, request, or environment condition that caused it. Use a narrow uncaught:exception handler only for a known accepted fault, and verify the user workflow still works.
Key Takeaways
- Read the underlying JavaScript message and first useful application stack frame before changing Cypress configuration.
- Repair missing data and rejected promises in application code with explicit loading and error states.
- Register intercepts before visiting and assert both the network response and visible result.
- Return false only for an exact, accepted exception; let all other exceptions fail.
- Place handlers inside cy.origin() for exceptions thrown on the secondary origin.
- Reproduce CI failures with the same browser, base URL, and backend response.
If you need to fix Cypress uncaught exception from application code, start with the first application stack frame: the failure appears when a page script throws during cy.visit(), after a click, or while an asynchronous request settles. Cypress reports that exception even when the Cypress command itself succeeded.
The following error originated from your application code, not from Cypress.
> Cannot read properties of undefined (reading 'name')
When Cypress detects uncaught errors originating from your application it will automatically fail the current test.
TL;DR
- Open the failed test in Cypress, expand the exception, and find the first frame in your application or dependency code. Reproduce it outside Cypress if possible.
- If an API response or configuration value is missing, fix the application path and assert on the request. Register
cy.intercept()beforecy.visit(); then usecy.wait('@alias'). - If a known third-party error is genuinely outside the behavior being tested, suppress only that exact message in a single spec or test. Return
falsefor that match and let every other error fail. - Run the focused spec in the same browser and environment as CI. A green test after blanket suppression is not evidence that the app works.
The smallest safe diagnostic command, from a project with Cypress already installed, is:
npx cypress run --spec "cypress/e2e/dashboard.cy.ts" --browser chrome
Replace the spec path with the failing file and choose a browser installed on your machine. The single-spec run guide covers related command choices.
What the Error Actually Means
The property-access line in the opening example is the underlying JavaScript error; your run will show its own message and stack. For broader suite design, see the Cypress modern test architecture guide.
Cypress observes uncaught exceptions from the application window and fails the current test by default. The prominent sentence identifies the source category, not the root cause. A preceding TypeError, ReferenceError, rejected promise, or dependency stack usually carries the actionable detail. The failure can occur before the first assertion, which is why moving an assertion or increasing defaultCommandTimeout rarely repairs it.
Read the command log in time order. If the exception follows visit, inspect startup initialization, route loaders, configuration, and scripts executed during page load. If it follows a click, inspect the event handler and the request it starts. If the page shows a login or error boundary while the test expects a dashboard, investigate that application state rather than treating the exception as test noise. Chrome DevTools can show the original stack and the response body that preceded the throw; use the Cypress debugging guide when a bundled stack is difficult to read.
The uncaught:exception event is a policy hook. Returning false tells Cypress not to fail that particular exception. It does not repair JavaScript state, retry a request, or guarantee later assertions remain meaningful. A global handler that always returns false can turn a broken render into a misleading pass. Cypress documents both the default failure and the conditional handler in its event catalog.
Root-Cause Decision Table
| Symptom | Likely root cause | Fix | Verification signal |
|---|---|---|---|
Cannot read properties of undefined immediately after visit |
Render reads data before it exists | Guard the state and render loading or error UI | Page reaches a defined state without an exception |
Uncaught (in promise) after a request |
Rejected fetch or promise has no handler | Check response.ok and catch at the async boundary |
Expected error UI appears for a failed response |
| Error appears only when an analytics or widget script loads | Known external script fault | Remove, upgrade, or isolate that script; narrowly ignore only if the product accepts it | First-party errors still fail the test |
| Failure begins after navigating to another origin | Listener registered in the wrong origin | Register the handler inside cy.origin() |
The intended secondary-origin case passes |
| Local pass, CI failure with missing configuration or a different response | Environment or backend contract drift | Provide test configuration and assert the response | Same spec passes locally and in CI |
| Test alternates between pass and exception | Race between startup, requests, and UI access | Make app state transitions explicit; wait on the relevant request | Repeated runs show the same stable UI |
Use the table to select a branch, then keep the failure visible while you apply that branch. The examples below use normal Cypress APIs. Their selectors and endpoints describe a small dashboard application; map those paths to your own app while preserving the test pattern.
1. Fix Cypress Uncaught Exception From Application Code Caused by Missing Data
A common startup crash is a component that assumes a profile object exists before the profile request resolves. The application expression profile.name throws if profile is undefined. Cypress correctly stops the test because the page is broken for a real user on the same loading path. The fix belongs in the component: represent loading, success, and failure explicitly.
For a React dashboard, this complete component reads a profile, checks the HTTP status, and renders a stable result. Save it as src/ProfilePanel.tsx and render <ProfilePanel /> on the dashboard route. The API must return JSON shaped like {"name":"Ava"}.
import { useEffect, useState } from 'react'
type Profile = { name: string }
type State =
| { kind: 'loading' }
| { kind: 'ready'; profile: Profile }
| { kind: 'error' }
export function ProfilePanel() {
const [state, setState] = useState<State>({ kind: 'loading' })
useEffect(() => {
const controller = new AbortController()
async function loadProfile() {
try {
const response = await fetch('/api/profile', {
signal: controller.signal,
})
if (!response.ok) throw new Error('Profile request failed')
const profile = (await response.json()) as Profile
if (typeof profile.name !== 'string') {
throw new Error('Profile response has no name')
}
setState({ kind: 'ready', profile })
} catch (error) {
if (!controller.signal.aborted) setState({ kind: 'error' })
}
}
void loadProfile()
return () => controller.abort()
}, [])
if (state.kind === 'loading') return <p>Loading profile</p>
if (state.kind === 'error') return <p role="alert">Profile unavailable</p>
return <h1>Welcome, {state.profile.name}</h1>
}
Register the response before visiting. This avoids a race in which the application requests the profile before the intercept exists. The test checks the actual visible state, not merely the absence of a Cypress exception.
describe('profile startup', () => {
it('renders a loaded profile', () => {
cy.intercept('GET', '/api/profile', {
statusCode: 200,
body: { name: 'Ava' },
}).as('profile')
cy.visit('/dashboard')
cy.wait('@profile').its('response.statusCode').should('eq', 200)
cy.contains('h1', 'Welcome, Ava').should('be.visible')
})
})
Save this spec as cypress/e2e/profile-startup.cy.ts. Verify with npx cypress run --spec "cypress/e2e/profile-startup.cy.ts" while your local application server is running. If the app uses another path, update both the component and intercept. The cy.intercept() guide explains matching and aliasing in more depth.
2. Handle an Uncaught Promise Rejection at Its Source
An async function can reject without a synchronous throw. For example, a click handler calls loadProfile() and discards its returned promise. If fetch rejects, JSON parsing fails, or an explicit throw occurs, the browser reports an unhandled rejection. Cypress may report it as an application exception. Returning false for every rejected promise conceals real outages.
Give the failure a product-visible outcome. The ProfilePanel above catches the request at the async boundary, aborts it on unmount, and renders Profile unavailable. Exercise the failure path, including HTTP failure, with a separate spec saved as cypress/e2e/profile-error.cy.ts:
describe('profile request failure', () => {
it('shows a useful error instead of crashing', () => {
cy.intercept('GET', '/api/profile', {
statusCode: 503,
body: { error: 'temporarily unavailable' },
}).as('profile')
cy.visit('/dashboard')
cy.wait('@profile').its('response.statusCode').should('eq', 503)
cy.get('[role="alert"]').should('contain.text', 'Profile unavailable')
})
})
The spec verifies both the failed response and the user-facing result. Run npx cypress run --spec "cypress/e2e/profile-error.cy.ts" after saving the adjusted spec under that path.
A 200 response can still contain malformed JSON or an unexpected shape. The component's name check covers that separate failure. Add a second test with body: {} and assert the same alert if your backend contract can drift. If the app must distinguish invalid data from a temporary service failure, use separate state variants and copy rather than collapsing them. This is a product behavior decision, not a Cypress configuration decision. See Cypress network stubbing for controlled response cases.
3. Repair a Request Race Instead of Raising Timeouts
A test can start on a route before the backend has returned essential data. Cypress retries query assertions, but it cannot make an unguarded application read safe. A fixed cy.wait(5000) only delays the observation and may still fail under slower CI load. Align test synchronization with the request that creates the state, and align application rendering with the state transition.
Use the real response as a spy when you want end-to-end coverage. This form of cy.intercept() does not stub the backend. It records the request and gives cy.wait() an explicit completion point:
describe('dashboard data', () => {
it('loads a real profile response', () => {
cy.intercept('GET', '/api/profile').as('profile')
cy.visit('/dashboard')
cy.wait('@profile').then(({ response }) => {
expect(response, 'profile response').to.exist
expect(response?.statusCode).to.eq(200)
expect(response?.body).to.have.property('name')
})
cy.contains('h1', /^Welcome, /).should('be.visible')
})
})
Save as cypress/e2e/profile-live.cy.ts and run npx cypress run --spec "cypress/e2e/profile-live.cy.ts". The assertion distinguishes a transport or server failure from a client render failure. If the alias never sees a request, inspect whether the app skipped the request, the URL pattern differs, or browser caching served the resource. Cypress intercepts network traffic, not a response already satisfied from cache.
The test should not use a live production account or an endpoint with unpredictable data. Provide a dedicated test environment with a controlled profile. If the backend cannot provide deterministic state, use the stubbed success and failure specs above for component behavior, then keep a smaller integration test for the live contract. This division makes a failing line tell you whether the error is in client state handling or the service boundary. The wait-with-alias guide covers waiting for multiple dependent requests.
4. Scope a Known Third-Party Exception
Sometimes an embedded widget throws a known exception while the first-party workflow remains valid. First investigate ownership and impact: can the dependency be upgraded, configured, delayed, or removed? If the team explicitly accepts the fault, use a narrow exception policy for only the relevant test. Capture the exact message from the failure output; do not use a broad substring such as TypeError or Script error.
This test-local listener is removed automatically when its test ends. The example matches a real browser ResizeObserver error. First establish that the observed layout loop is accepted for this workflow; then document the related issue and exact message in your repository. See MDN's ResizeObserver error discussion for its meaning.
describe('checkout with accepted widget fault', () => {
it('still completes the first-party confirmation', () => {
cy.on('uncaught:exception', (error) => {
if (error.message === 'ResizeObserver loop completed with undelivered notifications.') {
return false
}
})
cy.visit('/checkout')
cy.get('[data-cy="place-order"]').click()
cy.contains('h1', 'Order confirmed').should('be.visible')
})
})
Save as cypress/e2e/checkout-widget.cy.ts and run npx cypress run --spec "cypress/e2e/checkout-widget.cy.ts". Verify the safeguard with a separate deliberate first-party exception in a temporary test: it must still fail. Remove that temporary probe afterward. More usefully, review the CI failure screenshot and browser console whenever the widget's message changes, because a new message may indicate a new defect.
If many specs need the same policy, one Cypress.on('uncaught:exception', handler) registration in the support file is possible. A global listener persists across tests, so keep it narrow and register it once. Do not add a new global listener in every beforeEach; those listeners accumulate. Also keep the callback synchronous: Cypress commands such as cy.task() or cy.get() do not belong inside a Cypress.on callback. Use cy.on for the one-test case shown above.
5. Fix Cypress Uncaught Exception From Application Code on Another Origin
A handler registered in the primary origin does not automatically control an exception inside cy.origin(). When the failure occurs on an identity provider or another permitted origin, put the relevant handler inside that origin's callback. The origin must be a real site your test may visit, and the behavior should be verified there. Prefer repairing a first-party page even if it happens to be hosted on a second domain.
Here is the Cypress placement pattern for that same known, accepted ResizeObserver error on an example identity page. Replace the host and selectors with your actual test environment values, and keep the predicate only if that precise error occurs. The handler still lets unrelated errors fail.
describe('external sign-in page', () => {
it('loads the hosted sign-in form', () => {
cy.visit('/login')
cy.get('[data-cy="external-sign-in"]').click()
cy.origin('https://login.example.test', () => {
Cypress.on('uncaught:exception', (error) => {
if (error.message === 'ResizeObserver loop completed with undelivered notifications.') {
return false
}
})
cy.get('form').should('be.visible')
})
})
})
Save it as cypress/e2e/external-login.cy.ts after replacing the domain and selectors. Verify with npx cypress run --spec "cypress/e2e/external-login.cy.ts". If the navigation never reaches the expected host, correct the application link or test environment first. If it reaches the host but the exception differs, inspect the new stack; do not widen the predicate reflexively. The cross-origin Cypress guide explains command placement and origin boundaries.
Cypress's event documentation specifically notes the need for a handler inside cy.origin() when the exception originates there. This is a scope issue, not a reason to install an unconditional handler on both origins. A hosted login error that blocks authentication is a failing user journey even if a test can suppress the message.
6. Reproduce the CI and Docker Environment
When the same spec passes locally and fails in CI, compare what the application actually received. CI may omit an environment variable, reach a different API base URL, receive an HTML login redirect instead of JSON, or start the app before its backend is ready. A browser difference can also expose a real app bug. Cypress is reporting the resulting application exception, not necessarily causing the mismatch.
Keep configuration explicit. The following Cypress config points the test to a local server; provide APP_URL in CI if the server runs elsewhere. Save as cypress.config.ts.
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
baseUrl: process.env.APP_URL ?? 'http://127.0.0.1:3000',
supportFile: 'cypress/support/e2e.ts',
},
video: true,
})
Create cypress/support/e2e.ts as an empty file if your project does not already have one. Start the application and check that its URL is reachable before running the spec:
curl -f http://127.0.0.1:3000/dashboard
APP_URL=http://127.0.0.1:3000 npx cypress run --spec "cypress/e2e/profile-live.cy.ts" --browser chrome
The curl check proves that the page server responds; the Cypress assertions prove that the browser receives a usable API response and renders the page. An HTTP 200 from curl alone does not establish application health. Capture failing screenshots from cypress/screenshots; video: true enables run videos for timing analysis.
For a container run, use an official Cypress image tag that matches your installed Cypress version and includes the browser you select. The exact published tag format may include Cypress, Node, and browser versions, so choose a real tag from the official Docker image documentation rather than copying a fabricated number. One workable pattern uses cypress/included:<matching-published-tag> and a host application server accessible as host.docker.internal where your Docker platform supports it:
docker run --rm -v "$PWD:/e2e" -w /e2e \
-e APP_URL=http://host.docker.internal:3000 \
cypress/included:<matching-published-tag> \
--spec "cypress/e2e/profile-live.cy.ts" --browser chrome
On Linux, configure host networking or an explicit host gateway as appropriate for your environment. Confirm reachability from inside the container before blaming Cypress. The verification is the same focused spec, plus its response assertions; compare the captured HTTP body and status with the local run. For a broader CI setup, read the Cypress environment variables guide. Never print secret values just to diagnose an exception.
How to Verify the Fix
First rerun the smallest failing spec in the same browser. A valid fix has two properties: the original exception no longer appears, and the assertion for the intended user outcome passes. A test that merely reaches the final line after an unconditional return false has satisfied only the second half superficially.
Then exercise the negative path. The 503 profile spec should render an alert without an uncaught rejection. A malformed successful response should follow the application's explicit invalid-data branch. If you added a conditional exception handler, an unrelated first-party throw should still fail. This last check protects against a predicate that accidentally matches every error from a shared runtime.
Run the suite under the same command used in CI, including --browser, base URL, and environment values. If the bug appeared only in CI, compare screenshots, network response bodies, and application console output from both runs. Do not declare victory after a local stubbed response when the CI failure came from a live backend. Once focused tests pass, run the affected feature specs. Repeat only enough runs to establish that the originally intermittent path is stable; retries can reveal flakiness but do not fix application state. The flaky Cypress tests guide gives a wider investigation workflow.
Prevent It From Coming Back
Treat an uncaught application exception as a regression signal. In the app, model loading and error states explicitly, validate response shapes before reading nested fields, and catch promises at the boundary where an operation is started. On unmount, abort or otherwise ignore obsolete async work so stale responses cannot write into a newer view. Error boundaries can keep a React page usable after render errors, but they do not replace handling for failed fetches or event-handler promises.
In tests, register intercepts before the action that triggers the request. Assert a meaningful visible result and, when diagnosing transport, assert the response status and body. Keep stubbed tests for deterministic UI branches and a smaller live contract test for the real service. Use stable data attributes for actions whose accessible text changes with localization, while continuing to assert visible user messages where their content is the behavior under test.
Require review for additions to uncaught:exception handlers. Each exception should have an exact predicate, an owner, a reason it is accepted, and a test proving core functionality. Remove the handler when the dependency is fixed. Keep CI server readiness and configuration checks close to the test command. These habits turn the next failure into a precise application diagnosis instead of another unexplained Cypress red banner.
Interview Questions and Answers
Q: Why does Cypress fail on an application exception before an assertion? Cypress observes uncaught errors in the application window and treats them as test failures by default. The browser can throw during page load or an event handler before any assertion runs.
Q: What does returning false from uncaught:exception do? It prevents that exception from failing the test. It neither repairs the app nor guarantees the remainder of the user flow is correct.
Q: When would you suppress an exception? Only after confirming a specific, accepted external fault that does not invalidate the workflow. Match its exact message or another narrow property, then continue asserting the user outcome.
Q: Why register cy.intercept() before cy.visit()? The app may request data immediately during startup. Registering afterward can miss the request and produce a separate alias timeout.
Q: How do you distinguish an API failure from a render bug? Spy on the request and inspect its status and body, then assert the UI. A healthy response followed by a crash points toward client handling; a bad response points toward the service or configuration.
Q: Why might a handler work locally but fail inside cy.origin()? The secondary origin has its own execution scope. Register the handler inside that cy.origin() callback for errors thrown there.
Q: Does increasing a timeout fix the root cause? No. A timeout can allow a slow legitimate operation to finish, but it cannot make unsafe property access or an unhandled rejection correct.
Common Mistakes
- Adding
Cypress.on('uncaught:exception', () => false)to the global support file without a narrow predicate. This hides new first-party failures across the entire suite. - Matching generic text such as
undefinedorTypeError. Those strings describe many unrelated defects. - Installing a global handler in
beforeEach. Global listeners persist, so repeated registration creates duplicate callbacks. - Calling
cy.get(),cy.task(), or other Cypress commands inside aCypress.onlistener. Keep the callback synchronous. - Using
cy.wait(5000)to cover an app race. Wait on the relevant request and render a real loading state. - Stubbing every request while claiming the backend works. Pair deterministic UI tests with a live contract check.
- Assuming the error banner identifies the precise source. Read the JavaScript message, first useful stack frame, request, and rendered page together.
- Copying a Docker image tag or browser choice that does not match the installed project. Check the published tag and available browser before the run.
Conclusion
To fix Cypress uncaught exception from application code, trace the underlying JavaScript error and repair the application state or failing dependency. Use Cypress intercepts and visible assertions to prove the repaired path, then reproduce it in the browser and environment where it failed. Keep exception suppression as a documented, narrow exception to the rule so future crashes remain visible.
Interview Questions and Answers
What is Cypress telling you when it says an error originated from application code?
Cypress observed an uncaught error in the page under test and failed the current test. The banner identifies the source category, while the JavaScript error and stack identify the actual defect. I would inspect the first useful app frame and the preceding network activity before editing the test.
What does return false in an uncaught:exception callback change?
It prevents that particular exception from failing the Cypress test. It does not catch the error in the application or restore broken state. I would use it only for an exact accepted fault and still assert that the tested workflow completes.
How do Cypress.on and cy.on differ for exception handling?
Cypress.on registers a global listener that persists across tests; adding it in every beforeEach can accumulate handlers. cy.on is scoped to the current test and is automatically removed when that test ends. I choose the narrowest scope that covers the accepted behavior.
How would you diagnose a TypeError during cy.visit()?
I would expand the application stack, inspect startup data requests, and compare the browser console with a normal manual visit. Then I would check whether the component reads data before it exists or receives an unexpected response shape. The fix should add appropriate loading, validation, and error behavior.
Why should a network intercept be registered before a visit?
A page may fire its startup request as soon as it loads. If the intercept is registered after cy.visit(), Cypress can miss the request and cy.wait('@alias') can time out. Registering first makes the observation deterministic.
How do you separate a backend problem from a frontend crash?
I spy on the relevant request with cy.intercept(), wait for it, and inspect the response status and body. Then I assert the visible UI state. A valid response followed by a TypeError implicates client handling; a malformed or failed response implicates the contract or environment, though the client should still fail gracefully.
Why might a handler not catch an exception inside cy.origin()?
The secondary origin has a separate execution context. The handler must be registered inside the cy.origin() callback for an exception thrown there. I would also confirm the navigation reached the expected host and that suppressing the error does not hide a failed sign-in.
What evidence proves a CI-only exception is fixed?
The focused spec must pass in the same browser, application build, base URL, and backend conditions as CI. I would check the original error is absent, verify the relevant response and visible result, and preserve screenshots or logs for comparison. A local pass against a stub alone would not prove the CI path.
Frequently Asked Questions
What does 'The following error originated from your application code, not from Cypress' mean?
A script running in the tested page threw an uncaught exception or left a rejection unhandled. Cypress reports and fails the test by default. Read the JavaScript message and stack above the banner to identify the specific fault.
Should I return false for every Cypress uncaught exception?
No. An unconditional handler hides real application crashes across the tests it covers. Match only a documented, accepted error, and retain assertions for the actual user journey.
Where should I put a Cypress uncaught:exception handler?
Use cy.on inside one test when the exception policy applies only there. Register Cypress.on once in a support file only when a carefully scoped policy applies suite-wide. For an error inside cy.origin(), register the handler inside that origin callback.
Can cy.intercept fix an application exception?
It can make a request path deterministic and help prove whether the response caused the crash. The lasting fix is usually application error handling or a corrected backend response. Register the intercept before the action that starts the request.
Why does the error happen only in CI?
CI can use a different base URL, browser, configuration, or backend state. Compare the response status and body, inspect the application stack, and verify the server is ready before Cypress starts. Reproduce with the same browser and environment values.
Will increasing defaultCommandTimeout solve an uncaught exception?
No. More waiting cannot repair invalid property access or an unhandled rejected promise. Use a request alias to synchronize the test and make the application render explicit loading and failure states.
How can I verify that a narrow suppression is safe?
Assert the first-party workflow after the accepted exception. Also check that a different application exception still fails the test. Review the predicate whenever the dependency's message or behavior changes.
Related Guides
- How to Fix "Cypress failed to start" and cypress verify Errors
- How to Fix Cypress "cy.visit() failed trying to load"
- How to Fix Cypress "Element is detached from the DOM"
- How to Build a Cypress framework from scratch (2026)
- How to Fix "Playwright Test did not expect test() to be called here"
- How to Fix "The Cypress binary is missing" in CI