You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ping() stops retrying ECONNREFUSED after Node.js 24.20.0+ (upstream got bug) — start-server health check fails immediately instead of waiting for the dev server #1869
ping() stops retrying ECONNREFUSED after Node.js 24.20.0+ (upstream got bug) — start-server health check fails immediately instead of waiting for the dev server
Body
Describe the bug
src/ping.js's ping() (used for the start/health-check step) relies on got's built-in retry to wait out a dev server that hasn't started listening yet, special-casing ECONNREFUSED for a fast 1s retry:
if(error.code==='ECONNREFUSED'){return1000}
Since Node.js 24.20.0, this stops working entirely. got no longer retries a connection-refused failure at all — it fails after a single attempt with a generic ERR_SOCKET_CLOSED_BEFORE_CONNECTION instead of ECONNREFUSED, so calculateDelay is never even reached for a second attempt. Any workflow using this action's start/wait-on-style health check against a server that isn't up yet on the first ping will fail immediately instead of waiting.
Root cause
This is an upstream bug in got — filed in detail at sindresorhus/got#2469, including a Node.js bisect that pins it to nodejs/node#64847 (a _http_outgoing.js fix shipped in Node 24.20.0). Short version: Node's end() callback now correctly receives the connection error where before it silently never fired at all; got's retry state machine treats that callback firing as immediately terminal instead of retrying.
Confirmed this is not limited to the got@11.8.6 pinned here — it reproduces identically on current got@latest (15.1.0), so there's no drop-in version bump available to fix it today.
Reproduction
Any job on a Node.js 24.20.0+ self-hosted runner (or any environment where the action itself runs under Node 24.20.0+, since ping.js executes in the action's own Node process) using this action's health-check against a server that takes >0 attempts to come up:
fails on the first ECONNREFUSED instead of waiting for the server.
Suggested workaround (doesn't require a got fix)
Since got's own retry logic never gets invoked for this failure (only one connection attempt is ever made), fixing this doesn't strictly require waiting on the upstream got fix. ping() could catch ERR_SOCKET_CLOSED_BEFORE_CONNECTION at the outer call site and retry manually, e.g. treating it the same as an ECONNREFUSED for the purposes of the existing retry/backoff loop, until the upstream got bug is fixed and a compatible version can be adopted (got is also several breaking major versions ahead of the pinned 11.8.6 — v12+ is ESM-only — so that'll be a separate migration).
Happy to share our full repro/trace if useful — see sindresorhus/got#2469 for the complete writeup.
Correct, this is in a self-hosted custom image -- built from our modified fork of the official dockerfile, so its pretty similar but not inherited from it. I happened to do a rebuild yesterday to patch unrelated vulns and my 24.x pin floated to .20.0 .
Pinning to 24.19.0 does indeed work. Sorry that wasn't mentioned as clearly here as it was in the upstream issue.
Similar to your mention in the ESM issue, this isn't viable forever... the workaround patching ping() might buy some time though.
Thanks for the tip on wait-on -- I was already dreaming up ways to do this with curl or such, but this is cleaner.
This issue will need resolving one way or another. I would wait and see if there is a reaction to your issue sindresorhus/got#2469 first.
If that doesn't produce a solution, it may need a patch or a replacement of the ping utility.
I would imagine that it will be about a month before the GitHub-hosted runner gets updated to an incompatible Node.js >=24.20.0 version, so a fix would be fairly urgent otherwise the action will break for any Application Under Test server that isn't immediately available.
Just out of interest, which "official Dockerfile" are you referring to?
Unfortunately there does not appear to be a simple workaround for the bug sindresorhus/got#2469 exposed through Node.js 24.20.0.
This may need assistance from the Cypress.io team, however right now they are very tied up with preparing for the Cypress 16 release, so I would expect we'll have to wait a little.
Root cause: the crash isn't got mis-classifying ERR_SOCKET_CLOSED_BEFORE_CONNECTION as non-retryable — it's a bug in got's internal retry timer/p-cancelable cancellation logic, which fires a stale retry callback on an already-settled promise (onCancel handler attached after promise settled) when Node 24.20's new error timing (from [nodejs/node#64847](https://github.com/nodejs/node/pull/64847)) is involved. This reproduces even with plain got({retry:{limit:5}})`, no custom code needed.
On the official dockerfile point: https://github.com/actions/runner/blob/main/images/Dockerfile -- pretty minimal container with only enough components to get the gha runner binary to go.
The cloud runners (ubuntu-latest and such) obviously layer on the whole kitchen sink of additional tooling.
I see a PR and a few commits on the got side already, so that's encouraging.
Activity
MikeMcC399 commentedon Aug 27, 2026
@BowlesCR
Thanks for the issue description! I can reproduce using the ping debug instructions. Checking against a web server http://localhost:3000, like the one from examples/nextjs:
DEBUG='@cypress/github-action' node src/ping-cli.js http://localhost:3000This repo is using got@11.8.6 released on Dec 9, 2022, which is the last CommonJS release. See also #1647.
https://github.com/actions/runner/releases/tag/v2.337.0 in https://github.com/actions/runner/blob/397b032cbf865e9c3ddfab89d533ec19325e1273/src/Misc/externals.sh has updated to
NODE24_VERSION="24.19.0", so the issue is not yet visible on GitHub-hosted runners, since Node.js 24.20.0, released Aug 26, 2026 is not yet used.MikeMcC399 commentedon Aug 27, 2026
@BowlesCR
Are using this action in your own self-hosted GitHub runners?
Can you (temporarily) pin back to Node.js <=24.19.0 in that case?
MikeMcC399 commentedon Aug 27, 2026
An alternative to relying on the ping functionality would be to use wait-on which is shown in the README > Wait-on documentation section.
BowlesCR commentedon Aug 27, 2026
Correct, this is in a self-hosted custom image -- built from our modified fork of the official dockerfile, so its pretty similar but not inherited from it. I happened to do a rebuild yesterday to patch unrelated vulns and my 24.x pin floated to .20.0 .
Pinning to 24.19.0 does indeed work. Sorry that wasn't mentioned as clearly here as it was in the upstream issue.
Similar to your mention in the ESM issue, this isn't viable forever... the workaround patching
ping()might buy some time though.Thanks for the tip on
wait-on-- I was already dreaming up ways to do this withcurlor such, but this is cleaner.MikeMcC399 commentedon Aug 27, 2026
This issue will need resolving one way or another. I would wait and see if there is a reaction to your issue sindresorhus/got#2469 first.
If that doesn't produce a solution, it may need a patch or a replacement of the ping utility.
I would imagine that it will be about a month before the GitHub-hosted runner gets updated to an incompatible Node.js >=24.20.0 version, so a fix would be fairly urgent otherwise the action will break for any Application Under Test server that isn't immediately available.
Just out of interest, which "official Dockerfile" are you referring to?
cc: @jennifer-shehane
MikeMcC399 commentedon Aug 27, 2026
cypress-io/cypress#29775 mentions that Cypress might migrate to using the native fetch
This could be a possible approach here as well, if there is no viable solution with
got.MikeMcC399 commentedon Aug 28, 2026
Unfortunately there does not appear to be a simple workaround for the bug sindresorhus/got#2469 exposed through Node.js 24.20.0.
This may need assistance from the Cypress.io team, however right now they are very tied up with preparing for the Cypress 16 release, so I would expect we'll have to wait a little.
MikeMcC399 commentedon Aug 28, 2026
AI-assisted analysis said:
gotmis-classifyingERR_SOCKET_CLOSED_BEFORE_CONNECTIONas non-retryable — it's a bug ingot's internal retry timer/p-cancelablecancellation logic, which fires a stale retry callback on an already-settled promise (onCancelhandler attached after promise settled) when Node 24.20's new error timing (from [nodejs/node#64847](https://github.com/nodejs/node/pull/64847)) is involved. This reproduces even with plaingot({retry:{limit:5}})`, no custom code needed.BowlesCR commentedon Aug 28, 2026
On the official dockerfile point: https://github.com/actions/runner/blob/main/images/Dockerfile -- pretty minimal container with only enough components to get the gha runner binary to go.
The cloud runners (
ubuntu-latestand such) obviously layer on the whole kitchen sink of additional tooling.I see a PR and a few commits on the
gotside already, so that's encouraging.MikeMcC399 commentedon Aug 28, 2026
sindresorhus/got#2469 is showing resolved through sindresorhus/got#2470
We should monitor now for a new got release. I would however be surprised to see a backport of the fix from v15 to v11.
One option may be to upgrade to v15 once it contains the fix, which would mean converting to using ESM.
MikeMcC399 commentedon Aug 31, 2026
got@16.0.0, released on Aug 31, 2026, includes the fix "Retry on connection errors reported by
request.end()instead of failing the request"ERR_SOCKET_CLOSED_BEFORE_CONNECTIONimmediately on Node.js >=24.20.0 #1873 is a companion issueMikeMcC399 commentedon Aug 31, 2026
@BowlesCR
github-actions commentedon Sep 17, 2026
🎉 This issue has been resolved in version 7.4.4 🎉
The release is available on:
Your semantic-release bot 📦🚀