Title
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') {
return 1000
}
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:
- uses: cypress-io/github-action@v7
with:
start: npm start
wait-on: 'http://localhost:3000'
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.
Title
ping()stops retryingECONNREFUSEDafter Node.js 24.20.0+ (upstreamgotbug) — start-server health check fails immediately instead of waiting for the dev serverBody
Describe the bug
src/ping.js'sping()(used for thestart/health-check step) relies ongot's built-in retry to wait out a dev server that hasn't started listening yet, special-casingECONNREFUSEDfor a fast 1s retry:Since Node.js 24.20.0, this stops working entirely.
gotno longer retries a connection-refused failure at all — it fails after a single attempt with a genericERR_SOCKET_CLOSED_BEFORE_CONNECTIONinstead ofECONNREFUSED, socalculateDelayis never even reached for a second attempt. Any workflow using this action'sstart/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.jsfix shipped in Node 24.20.0). Short version: Node'send()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.6pinned here — it reproduces identically on currentgot@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.jsexecutes 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
ECONNREFUSEDinstead of waiting for the server.Suggested workaround (doesn't require a
gotfix)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 upstreamgotfix.ping()could catchERR_SOCKET_CLOSED_BEFORE_CONNECTIONat the outer call site and retry manually, e.g. treating it the same as anECONNREFUSEDfor the purposes of the existing retry/backoff loop, until the upstreamgotbug is fixed and a compatible version can be adopted (gotis also several breaking major versions ahead of the pinned11.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.