Skip to content
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

ECONNRESET after response #27916

Closed
ronag opened this issue May 26, 2019 · 71 comments
Closed

ECONNRESET after response #27916

ronag opened this issue May 26, 2019 · 71 comments
Labels
http Issues or PRs related to the http subsystem.

Comments

@ronag
Copy link
Member

ronag commented May 26, 2019

This was for me a bit of surprising behavior. You can get a connection error after a response.

const http = require('http')

const server = http.createServer(function (req, res) {
  req.on('data', () => {})
  setTimeout(() => {
    // Prematurely ending the request. Usually due to a 5xx error.
    res.statusCode = 200
    res.end()
  }, 1000)
})

server.listen(0, function () {
  const req = http.request({
    port: this.address().port,
    method: 'POST',
    path: '/'
  })
  req.on('response', res => {
    console.log("!", res.statusCode)
    clearInterval(interval)
  })
  const interval = setInterval(() => {
    req.write(Buffer.alloc(32))
  }, 1000)
})

Which sometimes prints:

! 200
events.js:167
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:111:27)
Emitted 'error' event at:
    at Socket.socketErrorListener (_http_client.js:391:9)
    at Socket.emit (events.js:182:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

I can't quite decide if this is wrong or right. I feel like it should either be a response or an error, not both... at least it should be consistent and happen just sometimes... Anyone, got any input?

@ronag
Copy link
Member Author

ronag commented May 26, 2019

I had to run it a few times to get it to fail:

^Cnxt$ nodemon test.js 
[nodemon] 1.19.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node test.js`
! 200
^Cnxt$ nodemon test.js 
[nodemon] 1.19.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node test.js`
! 200
^Cnxt$ nodemon test.js 
[nodemon] 1.19.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node test.js`
! 200
events.js:167
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:111:27)
Emitted 'error' event at:
    at Socket.socketErrorListener (_http_client.js:391:9)
    at Socket.emit (events.js:182:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
[nodemon] app crashed - waiting for file changes before starting...

@ronag
Copy link
Member Author

ronag commented May 26, 2019

Adding a abort after response seems to stop the error from being emitted. Even though #20077 is not merged.

@ronag
Copy link
Member Author

ronag commented May 26, 2019

I think we should always emit a ECONNRESET error if we get a response to a request which hasn't .end():ed?

@lpinca
Copy link
Member

lpinca commented May 26, 2019

It probably happens because the client writes right after the server destroyed the socket (after the response was written).

@lpinca lpinca added the http Issues or PRs related to the http subsystem. label May 26, 2019
@ronag
Copy link
Member Author

ronag commented May 26, 2019

Should this be considered a bug? I can see a lot of potential cases where this can cause surprises... especially where developers might think the requests are "ok", "done", "no worries" after a response and might e.g. remove the error event listener.

@lpinca
Copy link
Member

lpinca commented May 26, 2019

I don't know It's a race condition probably hard to fix.

@ronag
Copy link
Member Author

ronag commented May 26, 2019

@lpinca How about one of two possible simple mitigations?

  • Don't emit error after response.
  • Don't emit response if request is not ended.

@lpinca
Copy link
Member

lpinca commented May 26, 2019

I think both are not viable because there is no guarantee that the full response will be written in a single chunk.

@ronag
Copy link
Member Author

ronag commented May 26, 2019

@lpinca just to be clear, what do you think is the correct behavior here?

@lpinca
Copy link
Member

lpinca commented May 26, 2019

Not sure if correct but it makes sense.

@ronag
Copy link
Member Author

ronag commented May 26, 2019

Do you mean the current behavior?

@lpinca
Copy link
Member

lpinca commented May 26, 2019

Yes, it's weird but I can see why it happens.

@lpinca
Copy link
Member

lpinca commented May 26, 2019

cc: @nodejs/http

@ronag ronag mentioned this issue May 26, 2019
@addaleax
Copy link
Member

@lpinca It’s not obvious to me why there’s an ECONNRESET in the first place… why is the socket destroyed rather than cleanly .end()ed?

@lpinca
Copy link
Member

lpinca commented May 26, 2019

Good point, should not be destroyed my bad, seems like a bug then.

@lpinca
Copy link
Member

lpinca commented May 26, 2019

@ronag what version of Node.js? OS?

@ronag
Copy link
Member Author

ronag commented May 26, 2019

Node 10.13.0 & OSX

@lpinca
Copy link
Member

lpinca commented May 26, 2019

Thanks, I will test tomorrow on macOS, right now I'm using Node.js v10.15.3 on Fedora 30 and I can't reproduce.

@ronag
Copy link
Member Author

ronag commented May 26, 2019

I was able to reproduce on 10.15.3

Now using node v10.15.3 (npm v6.4.1)
nxt$ open test.js ^C
nxt$ node test.js 
! 200
^C
nxt$ node test.js 
! 200
^C
nxt$ 
nxt$ node test.js 
! 200
^C
nxt$ node test.js 
! 200
^C
nxt$ node test.js 
! 200
^C
nxt$ node test.js 
! 200
^C
nxt$ node test.js 
! 200
^C
nxt$ node test.js 
! 200
events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:111:27)
Emitted 'error' event at:
    at Socket.socketErrorListener (_http_client.js:392:9)
    at Socket.emit (events.js:189:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

@ronag
Copy link
Member Author

ronag commented May 26, 2019

I tried fedora 30 in docker and was unable to reproduce

@lpinca
Copy link
Member

lpinca commented May 26, 2019

Expand
$ while [ $? == 0 ]; do node test.js; done
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
! 200
^C

I've only added setTimeout(() => server.close(), 100); after clearInterval() to make it exit.

@ronag
Copy link
Member Author

ronag commented May 26, 2019

Using the following example I am able to reproduce the scenario every time on OSX:

const http = require('http')

const server = http.createServer(function (req, res) {
  res.end()
})

server.listen(0, function () {
  const req = http.request({
    port: this.address().port,
    method: 'POST',
    path: '/'
  })
  req.on('response', res => {
    req.write(Buffer.alloc(32))
  })
  req.write(Buffer.alloc(32))
})

@ronag
Copy link
Member Author

ronag commented May 26, 2019

Found even smaller repro (updated previous comment)

@ronag
Copy link
Member Author

ronag commented May 26, 2019

Seems related to OSX. Can't reproduce on debian (docker node:latest) either.

@himself65
Copy link
Member

Using the following example I am able to reproduce the scenario every time on OSX:

const http = require('http')

const server = http.createServer(function (req, res) {
  res.end()
})

server.listen(0, function () {
  const req = http.request({
    port: this.address().port,
    method: 'POST',
    path: '/'
  })
  req.on('response', res => {
    req.write(Buffer.alloc(32))
  })
  req.write(Buffer.alloc(32))
})

It's crashed on my Windows with node v10.13.5

C:\Users\himself65\Desktop\Github\test>node index2.js
events.js:174
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:111:27)
Emitted 'error' event at:
    at Socket.socketErrorListener (_http_client.js:392:9)
    at Socket.emit (events.js:189:13)
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

@lpinca
Copy link
Member

lpinca commented May 27, 2019

Can reproduce on both Windows and macOS but it seems my original guess is correct.

The socket is destroyed when res.end() is called

socket.destroySoon();

because the connection is not kept alive. Perhaps we should make req.write() throw/emit an error if used after the 'response' event is received.

@ronag
Copy link
Member Author

ronag commented May 27, 2019

because the connection is not kept alive. Perhaps we should make req.write() throw/emit an error if used after the 'response' event is received.

Sounds like a good idea. However, it still leaves things a bit tricky to use:

const req = http.request({ ... })

src
  .pipe(req)
  .on('error', onError)
  .on('response', res => {
    src.unpipe(req) // IMPORTANT or you might get unexpected errors
  })

Could maybe req.write be a noop (i.e. dump) or return false after a response has been received? (yes, it sounds bad in my ear as well)

@aPoCoMiLogin
Copy link

aPoCoMiLogin commented May 28, 2019

For me it also occurs on linux (docker node:11.15.0-alpine image), at really low rate, but still target server had responded in time with 200 status code, but still got "socket hang up".

@vincenzo-mazzotta
Copy link

Somebody has news about this bug?
We have this problem in GCP with CloudRun nodejs app on alpine and calling mongodb.
Thanks a lot for your support.

@mcollina
Copy link
Member

@ronag is this problem still there?

@vincenzo-mazzotta
Copy link

We have in a GCP cloud run project nodejs/mongodb with alpine

@MarcGodard
Copy link

I still get this with nodejs.

@Nefsen402
Copy link

We're getting this exact ECONNRESET error on our actual nodejs production deployment. However, when trying to run the test script, I'm unable to reproduce on my archlinux dev box. Even if I upload the script to our Ubuntu 20.04 production server, I still can't reproduce there with the test script.

@Nefsen402
Copy link

To elaborate on the previous comment, It doesn't actually look like we're running into the error in the context of a network socket. We're running into the error through two node processes doing IPC over file descriptors. If the child process fails in some way, sometimes the parent process will fail with the ECONNRESET. No useful stack traces are generated.

@im6
Copy link

im6 commented Mar 4, 2022

I am also seeing this error.
strangely that it only happens on one of my js project, i see this constantly when i use webpack-dev-server
i am using mac M1 version 12.1
node version 16.14

node:events:498
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20)
Emitted 'error' event on ClientRequest instance at:
    at TLSSocket.socketErrorListener (node:_http_client:442:9)
    at TLSSocket.emit (node:events:520:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  errno: -54,
  code: 'ECONNRESET',
  syscall: 'read'
}

@Anatanokami
Copy link

Anatanokami commented Mar 9, 2022

I am also seeing this error. strangely that it only happens on one of my js project, i see this constantly when i use webpack-dev-server i am using mac M1 version 12.1 node version 16.14

node:events:498
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20)
Emitted 'error' event on ClientRequest instance at:
    at TLSSocket.socketErrorListener (node:_http_client:442:9)
    at TLSSocket.emit (node:events:520:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  errno: -54,
  code: 'ECONNRESET',
  syscall: 'read'
}

+1 here -- exactly the same error when running webpack or webpack-dev-server. Both with 16.14.0 and 17.7.0.

@Anatanokami
Copy link

@im6 I was able to fix this on my end by removing the import-statement for the Google Font. Both in my repo as well as in the one you linked to this issue. Seems to be a problem with one of the style loaders rather than webpack or Node.

@im6
Copy link

im6 commented Mar 15, 2022

@im6 I was able to fix this on my end by removing the import-statement for the Google Font. Both in my repo as well as in the one you linked to this issue. Seems to be a problem with one of the style loaders rather than webpack or Node.

Yes you are right @Anatanokami .
It is @import() function in .less file that broke the app and emit the error.
Thanks for pointing out.

@jvinhit
Copy link

jvinhit commented Mar 31, 2022

I am also seeing this error.
Error: read ECONNRESET\n at TLSWrap.onStreamRead (internal/stream_base_commons.js:209:20

@meznaric
Copy link

Upgrading Node to 16.15 from 16.14 solved the issue for us.

Specifically it was happening on webpack start.

@mcollina
Copy link
Member

@ronag wdyt?

@jmealo
Copy link

jmealo commented Feb 23, 2023

I encounter a ton of ECONNRESET on my M1 Mac that other developers can't replicate on x86. I think it may be highly timing depending and my machine is able to reproduce it easily.

@bnoordhuis
Copy link
Member

I can't reproduce this with v19.x on macOS. This issue is almost 4 years old and the http module has gone through a lot of changes in those years. The original bug almost certainly has been fixed.

Me-too commenters: if you still hit this bug in the latest v18.x or v19.x, please file a new issue and include steps to reproduce.

If you're seeing it with v16.x or v14.x, you may be out of luck. Those release lines are in maintenance mode and near their end of life.

@bnoordhuis bnoordhuis closed this as not planned Won't fix, can't repro, duplicate, stale Feb 27, 2023
@jmealo
Copy link

jmealo commented Feb 27, 2023

@bnoordhuis I made the same assumption with the new HTTP implementation but this seems to one level lower (possibly socket related race condition having to do with event handlers). It's difficult to reproduce in isolation but easy to reproduce in established projects that I can't share the source code to. We should definitely get a new issue opened up even without reproducible steps so that folks don't go crazy trying to debug and can share/collaborate.

@aryanbhatt-clvt
Copy link

Is there a solution for this ? Because it happens on my windows

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.