Recommended Free Tools
The error has one underlying cause: an onRequest-style callback does not run in Cypress’s normal command queue. Cypress commands such as cy.wait(), cy.task(), cy.get(), and cy.request() are queued commands, so they cannot be executed from that callback. Keep the handler synchronous, use its request/response APIs, and hand any data you need to the test chain with an alias or a plain variable.
Some developers call a cy.intercept() route handler an “onRequest handler.” Others mean a Cypress.on() event listener. The names differ, but the practical rule is the same: do not put Cypress commands in either callback. Queue them later, after the interception has been yielded to the test.
The short fix
Move every cy.* command out of the callback. Inside the callback, inspect or change the supplied req object with ordinary JavaScript and synchronous Chai assertions. Set an alias or save plain data. Then use cy.wait(), cy.task(), or another Cypress command in the test chain after the application has made the request.
The pattern that causes the error
cy.intercept('POST', '/users', (req) => {
cy.task('recordRequest', req.body)
cy.wait(1000)
cy.get('[data-cy=message]').should('be.visible')
}).as('createUser')
Those calls are not merely in the wrong order. The callback is running during network handling, outside the queue that executes commands in the test body. Adding await does not change that execution context.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems#1 Best Overall
A supported route-handler pattern
cy.intercept('POST', '/users', (req) => {
expect(req.body).to.include('Acme Company')
req.headers['x-test-mode'] = 'true'
req.alias = 'createUser'
}).as('users')
cy.wait('@createUser')
.its('request.body')
.should('include', 'Acme Company')
The handler performs synchronous work on the request. The later cy.wait() runs in the test’s command chain, where Cypress commands are supported.
Understand which callback you are using
cy.intercept() route handler
The third argument to cy.intercept() is a route handler. Cypress invokes it when a matching browser request is intercepted. It receives a request object and can inspect or alter the request, provide a stubbed response, or attach response lifecycle listeners. It is not a place to start a second Cypress command chain.
Cypress.on() event listener
A listener registered with Cypress.on() is a Cypress event callback, not a test-body callback. Cypress documents that these callbacks run outside its normal command queue; Cypress commands, assertions, and cy.task() are not supported there. If an event callback must communicate with a test, capture simple data and consume it later in the test chain.
Execution-context comparison
| Code location | When it runs | Use here | Do not use here |
|---|---|---|---|
cy.intercept() route handler |
During matching request/response processing | Read or mutate req; synchronous JavaScript; req.reply(), req.continue(), req.destroy(), req.redirect(), and req.on() |
cy.get(), cy.wait(), cy.task(), cy.request(), or other queued commands |
Cypress.on() listener |
When the Cypress event fires | Small synchronous handlers; capture plain values for later | Cypress commands, Cypress assertions, and cy.task() |
Test body and .then() callback |
In Cypress’s command queue | Assertions, aliases, cy.wait(), cy.task(), and follow-up commands |
Returning a conflicting value while also queuing commands |
Use the request and response APIs inside the handler
Inspect or change a request synchronously
Route handlers can read the request body, headers, URL, and method. They can add a test header, set an alias, or make a synchronous assertion before the request is sent.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
cy.intercept('PUT', '/profiles/*', (req) => {
expect(req.method).to.equal('PUT')
expect(req.url).to.match(//profiles//)
expect(req.body).to.have.property('displayName')
req.headers['x-test-mode'] = 'true'
req.alias = 'updateProfile'
}).as('profileUpdate')
Use ordinary JavaScript for transformations. If a value must come from a Cypress command, do not try to obtain it inside this callback; arrange for that value before registering the intercept or use it after the interception has been yielded.
Stub, pass through, fail, or redirect
req.reply()supplies a stubbed response.req.continue()allows the real request to reach the server and can receive a response callback.req.destroy()forces a network error.req.redirect()returns a redirect response.req.on()attaches response-event handlers.
cy.intercept('GET', '/api/account', (req) => {
req.continue((res) => {
expect(res.statusCode).to.equal(200)
res.headers['x-seen-by-test'] = 'true'
})
})
The response object exposes body, headers, statusCode, and statusMessage. Changes to the first three can affect the response delivered to the browser in the supported response phases.
Rank #2
Choose the response phase deliberately
before:responseruns before response handlers and beforereq.continue()response callbacks.responseruns after those handlers but before the response is sent to the browser.after:responseruns after delivery, so it cannot change what the browser receives.
These callbacks still use the interception lifecycle APIs, not Cypress commands.
Move asynchronous test work back into the command chain
Use an alias and cy.wait()
The most robust handoff is to assign an alias to the intercepted request and wait for it in the test body. The yielded interception contains the request and, when available, the response.
cy.intercept('POST', '/users', (req) => {
req.alias = 'createUser'
}).as('users')
cy.get('[data-cy=submit]').click()
cy.wait('@createUser').then((interception) => {
expect(interception.request.body).to.have.property('email')
cy.task('recordRequest', interception.request.body)
})
Here, the callback only labels the request. The cy.wait(), the assertion in its .then(), and cy.task() all execute after Cypress has yielded the interception to the command queue.
Capture plain data when a later chain needs it
let capturedBody
cy.intercept('POST', '/users', (req) => {
capturedBody = req.body
req.alias = 'createUser'
})
cy.get('[data-cy=submit]').click()
cy.wait('@createUser').then((interception) => {
expect(interception.request.body).to.deep.equal(capturedBody)
cy.task('recordRequest', interception.request.body)
})
Only store ordinary values in the outer variable. Do not store a Cypress command or pretend that assigning a command’s return value gives you its eventual result.
Where each commonly attempted command belongs
cy.wait()
Use cy.wait('@alias') after the application action that triggers the request. A wait inside the route handler would deadlock the intended flow: the handler is already processing the request, while cy.wait() needs the test queue.
cy.task()
Use cy.task() in the test chain to perform Node-side work such as writing a file or recording a request. Put the data on an alias or in the yielded interception, then call the task in a later .then().
Rank #3
cy.request()
cy.request() is for making a direct HTTP call from Cypress’s Node process, commonly for setup, seeding, or API verification. It must be chained from cy, and it bypasses routes defined with cy.intercept(). It is therefore neither a substitute for a command inside the handler nor a way to intercept the browser request currently being processed.
await
Cypress commands are not Promises and cannot be awaited. Writing await cy.wait('@createUser') does not make the command run inside the callback, and marking the route handler async does not move it into Cypress’s queue. Use Cypress chaining and .then() instead.
A complete, reliable test flow
- Register the intercept in a supported test context, before the user action that sends the request.
- Keep the route handler synchronous. Inspect
req, make immediate assertions, modify request properties, or choose a route response. - Assign an alias when the test needs to wait for or inspect that request.
- Perform the application action, such as clicking Submit.
- Call
cy.wait('@alias')in the test body. - Use the yielded interception in
.then()or with chained assertions, and run anycy.task()there.
it('submits a user and records the request', () => {
cy.intercept('POST', '/users', (req) => {
expect(req.body).to.have.property('company', 'Acme Company')
req.headers['x-test-mode'] = 'true'
req.alias = 'createUser'
})
cy.get('[data-cy=company]').type('Acme Company')
cy.get('[data-cy=submit]').click()
cy.wait('@createUser').then((interception) => {
expect(interception.request.headers['x-test-mode']).to.equal('true')
cy.task('recordRequest', interception.request.body)
})
})
Fix common failures
“Cannot call cy.* outside a running test”
Cause: A command was called from a route handler or a Cypress.on() listener.
Fix: Replace the command with synchronous request logic, save the needed value, and consume it after cy.wait() or in a later test-chain callback.
The command appears to run too early or never runs
Cause: The callback’s execution is not part of the queue, so Cypress cannot schedule the command relative to the rest of the test.
Fix: Set an alias in the handler and wait for that alias after the browser action. Register the intercept before the action so the request cannot pass before the route exists.
Rank #4
cy.task() is needed to persist request data
Cause: The task was placed in the callback.
Fix: Yield the interception first, then call cy.task() from cy.wait('@alias').then(...). Pass a serializable body or selected fields rather than the live request object.
An assertion inside the handler fails unexpectedly
Cause: The assertion depends on a value that would require a Cypress command, or the handler is matching more requests than intended.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Fix: Narrow the intercept method and URL pattern. Keep only immediate assertions on req in the handler; move DOM assertions and command-derived values to the test chain.
A response change is not visible in the browser
Cause: The response was changed in after:response, which runs after delivery, or the handler never called the appropriate continuation/stub API.
Fix: Make supported response edits in req.continue(), before:response, or response, and use req.reply() when supplying a stub.
“Cypress detected that you returned a promise…” or a conflicting return error
Cause: A callback queued a Cypress command and also returned a different value. Cypress commands execute later, so that return value conflicts with the queued work.
Fix: Remove the explicit non-undefined return, or move the command into the test chain. Do not use a return statement to make a Cypress command synchronous.
Reliability and timing considerations
Keep handlers fast and deterministic
A route handler runs on the request path. Do parsing, validation, header changes, and response decisions there; defer file I/O, database work, DOM queries, and other queued activity until after the interception is yielded. This keeps the browser request lifecycle predictable.
Do not use arbitrary delays to repair queue problems
A delay such as cy.wait(1000) inside the callback cannot fix the context error. If the test needs to wait for a request, wait on its alias in the command chain. If the application needs time to render after the response, assert on the resulting UI after the network wait.
Separate API setup from browser interception
Use cy.request() for direct server setup or verification, understanding that it runs from Node and bypasses cy.intercept(). Use cy.intercept() when the browser request itself must be observed, modified, stubbed, or synchronized with the test.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Or skip the browser setup
If your goal is to capture a page image for test evidence, documentation, or a visual check rather than exercise Cypress’s browser interception, ScreenshotNeo provides a single screenshot API request. Its cleanup step accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Only clean shots are billed, while bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are free, with the result identified by X-Page-Verdict and X-Billed headers. The same service also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Use the API key and URL in the request below; the ScreenshotNeo documentation lists the request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo supports PNG, JPEG, WebP, and PDF output, full-page and element captures, device and viewport settings, retina scale, dark mode, custom CSS and JavaScript, selector waits, network-idle waits, request blocking, custom headers and cookies, geolocation and timezone, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, caching with a chosen TTL, and a usage API. Every feature is included on every plan: 1,000 shots per month are free with no card; paid plans are $5 for 3,000, $15 for 15,000, $39 for 60,000, $99 for 250,000, and $249 for 1,000,000, with yearly billing providing two months free. Create a free ScreenshotNeo account to start with the 1,000 monthly shots.
Frequently Asked Questions
Should a route handler be declared with async?
It is unnecessary for Cypress command handling. Keep the callback synchronous and use the interception APIs; an async declaration does not put Cypress commands into the command queue.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchCan a response be changed after it reaches the browser?
No. Changes made in after:response occur after delivery and cannot affect the browser. Make supported edits in the earlier response phases instead.
Why might a direct API check miss an intercept?
cy.request() runs from Cypress’s Node process and bypasses routes defined with cy.intercept(). It verifies the endpoint directly rather than reproducing the browser request path.
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.

