Skip to content

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

Closed

Description

@BowlesCR

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.

Activity

  1. added theissue type on Aug 27, 2026
  2. MikeMcC399 commented on Aug 27, 2026

    @MikeMcC399
    Collaborator

    @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:3000
    Node.js Result
    24.19.0 waits
    24.20.0 fail
    26.6.0 waits
    26.7.0 fail
    26.8.0 fail
    26.8.1 fail

    This 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.

  3. MikeMcC399 commented on Aug 27, 2026

    @MikeMcC399
    Collaborator

    @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?

  4. MikeMcC399 commented on Aug 27, 2026

    @MikeMcC399
    Collaborator

    An alternative to relying on the ping functionality would be to use wait-on which is shown in the README > Wait-on documentation section.

  5. BowlesCR commented on Aug 27, 2026

    @BowlesCR
    Author

    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.

  6. MikeMcC399 commented on Aug 27, 2026

    @MikeMcC399
    Collaborator

    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

  7. MikeMcC399 commented on Aug 27, 2026

    @MikeMcC399
    Collaborator

    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.

  8. MikeMcC399 commented on Aug 28, 2026

    @MikeMcC399
    Collaborator

    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.

  9. MikeMcC399 commented on Aug 28, 2026

    @MikeMcC399
    Collaborator

    AI-assisted analysis said:

    • 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.
  10. BowlesCR commented on Aug 28, 2026

    @BowlesCR
    Author

    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.

  11. MikeMcC399 commented on Aug 28, 2026

    @MikeMcC399
    Collaborator

    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.

  12. MikeMcC399 commented on Aug 31, 2026

    @MikeMcC399
    Collaborator

    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"

  13. MikeMcC399 commented on Aug 31, 2026

    @MikeMcC399
    Collaborator

    @BowlesCR

  14. github-actions commented on Sep 17, 2026

    @github-actions

    🎉 This issue has been resolved in version 7.4.4 🎉

    The release is available on:

    Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions