New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XHR requests are cancelled and re-issued if they don't return in 30 seconds #6426
Comments
Have experienced this behavior as well on version 4.4.0 (likely older versions as well but was not caught until recent update) at both timeouts of 60 seconds and 25 seconds (after seeing this open issue). Requests are made exactly 60 or 25 seconds apart, respectively, after the timeout occurs when the test with the related XHR request fails. |
I can recreate this behavior from the repo provided. I believe this is because of how the client behaves on aborted requests. Gonna have to check on this though. If Cypress does not see the request within the given timeout, we abort the XHR request (so that the tests can continue to the next assertion/test). Clients are meant to retry requests if the connection closes before receiving a status from the server.
|
@jennifer-shehane I think the part right before where you started the quote is also very relevant (emphasis mine):
In this case the client is already "directly connected to an HTTP/1.1 origin server", right? It sent the request and is only waiting for the server to reply. I can see why if the client has not yet established a connection to the origin server, and it sees a connection close, then it should retry the request, because it's guaranteed that the server has not received it before. But once it established a connection with the server, I'd imagine that it should not retry a request that it already sent, no matter the kind of failure it sees (including a closed connection), otherwise the spec would be calling for potentially issuing POST requests to the server several times, which I'm not sure makes sense... Besides that, note that the timeout I specified for Cypress in the repro was 35 seconds, so Cypress should not be aborting these requests at the 30 second mark, correct? |
So, I discussed this with the team and my assumptions before were incorrect. The Cypress proxy layer expects some sort of response within 30 seconds or else it will retry under the hood - this is controlled by the request timeout. This attempts to mimic the way the browser retries, but it seems in this case that this is not mimicking the behavior of the Chrome browser. cc @flotwig |
The proxy layer will retry certain failures, but timeouts are not one of them, precisely because it does not match the browser's behavior: cypress/packages/server/lib/request.js Line 15 in a27bd34
However, I tested in Chrome and Chrome does not issue the second request, so it is most likely a bug in Cypress. |
I wonder if the browser would retry on an ECONNRESET... since that one could happen after the client has finished sending the HTTP request to the server, and the request might thus be getting processed, it seems to me like an ECONNRESET caused by an intermediate proxy closing its connection should not be cause for the client to retry. If any of the others happen (except EPIPE, I'm not entirely sure on that one), then the client has a guarantee that the server did not receive the request, and thus it's safe to retry, but ECONNRESET (and potentially EPIPE) seem to me like they don't fit that pattern. |
I'm also seeing this issue if my API takes ~30sec or longer. As soon as I adjusted my API to 25 seconds, it worked. Something is in Cypress that doesn't like waiting longer than 28sec or so. |
My solution was to significantly increase the global timeout in the cypress.json configuration. |
@alexvy86 Not sure its the same issue in my side, It got |
By default, backend requests are killed after cypress/packages/server/lib/server.js Line 204 in 1288ecf
Probably the fix is to remove the timeout on the backend (or change the timeout to be equal to Chrome's, which I believe is 120000), since it should not actually be coupled to @alexvy86 Workaround: Increase |
That's the effect that this is supposed to have, right? Per the documentation here, I'll try to make some to test by changing
By the backend you mean the server that gets called? That one has no explicit timeout in the test code I uploaded, nor in the environment where we first encountered this issue. I'm certain it's not the server terminating the connection that causes this behavior. |
Adding |
@flotwig Also I found same scenario with How I solve this problem I change to use |
@alexvy86 The @Rey-Wang The problem should apply to any network request, so that sounds like this issue |
Just to be clear, the timeout used by the proxy to terminate requests (the one in I got a bit confused when you said
I took it to mean they were the same thing, that the timeout we pass to |
It is the same The So you can see how this is a problem if you have a request you're waiting for that lasts longer than |
Got it, got it. So... what the documentation for And after thinking about it, it actually makes sense that |
💯 yes exactly |
The code for this is done in cypress-io/cypress#7787, but has yet to be released. |
@flotwig one last thing about this topic... I think the documentation for |
Actually, if I understand the PR correctly, you made it so the global timeout isn't applied to all requests... and that'll let them respect the |
Released in This comment thread has been locked. If you are still experiencing this issue after upgrading to |
@alexvy86 yep, now the documentation's claims about |
Current behavior:
When an XHR request takes more than 30 seconds to complete, Cypress cancels the request and issues it again. Depending on what the server does with those requests, this can cause all sorts of problems (e.g. creating duplicate things, or directly resulting in failed tests when the server replies with a 5xx error that it wouldn't have issued if the request hadn't been sent twice).
Desired behavior:
Cypress should never retry an XHR request on its own, because it cannot know what the effects of a duplicate request could be.
Test code to reproduce
cypress-bug.zip (also contains the video generated by Cypress for the failing test).
I used the code in that zip file to generate the screen recording below. The idea is to set up a web server with an API call that takes 34 seconds to reply, then run a Cypress test that visits the home of the web server (which just issues an XHR call to the API method) and waits for the API response, with a timeout longer than the time it takes for the API to respond. You'll see on the logs of the webserver that the API method gets called twice, the second one pretty much exactly 30 seconds after the first one.
To work with the code:
npm i
on both thewebserver
andtester
foldersnode index.js
from thewebserver
foldernpm run cypress
from thetester
folderYou'll see that the webserver logs one call to
/
, one to/api
, and ~30 seconds later another call to/api
. Cypress then reports a failed test.Versions
The issue happens starting with Cypress 3.8.0 (tested with 3.6.1, 3.7.0, 3.8.0, 4.0.1)
Windows 10
Headless tests with Electron
Additional information
I originally discovered this with the Cypress Docker images. In the project where I found it I could even see Nginx reporting that the client (Cypress running tests) had closed the connection, right before receiving the duplicate request.
Screen recording of the code above showing the issue.
The text was updated successfully, but these errors were encountered: