actions / actions/runner

Listener silently exits broker-reconnect loop after AAD credential-refresh (ghost-busy)

Open
#4,446 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
6.3k
Forks
1.4k
Avg merge
1d 16h
Merged PRs (30d)
24

Description

Describe the bug

On self-hosted macOS runners (v2.334.0), after a long-running session encounters a broker disconnect followed by a credential refresh, the Runner.Listener process can silently exit its broker-reconnect loop. The OS process stays alive (parked on a pthread_cond_wait) but holds no ESTABLISHED TCP socket to the broker, writes no further diag log entries, and accepts no new jobs.

Critically: the broker side of the agent state still shows the runner as busy from its last in-flight job, so the queue stalls behind the phantom. Only launchctl bootout + bootstrap of the LaunchAgent (or equivalent hard process restart) clears the state. The runner does not self-recover.

We observed this simultaneously on 4 of 4 macOS-arm64 runners on the same host within a 32-minute window, freezing our queue for ~4 hours.

Runner

  • Version: v2.334.0
  • OS: macOS 26.3.1 (Apple Silicon, M3 Ultra)
  • Service supervision: launchd LaunchAgent (actions.runner.<owner-repo>.<name>)
  • Repo-scoped (not org-scoped) registration
  • 4 ephemeral=false runners on one host, ~10 other runners for a different repo on the same host

Expected behavior

On any unrecoverable broker session error, the listener should either:

  1. Surface a fatal error and exit (so the supervisor restarts it), or
  2. Keep retrying the reconnect indefinitely with visible diag log entries.

Actual behavior

The listener handled dozens of routine SocketException (89): Operation canceled events successfully across an ~11-hour run (these are the normal long-poll cancel/retry pattern). Then at a credential-refresh boundary it logged this sequence and went completely silent:

[2026-05-22 08:36:41Z INFO RSAFileKeyManager] Loading RSA key parameters from file <path>/.credentials_rsaparams
[2026-05-22 08:36:42Z INFO GitHubActionsService] AAD Correlation ID for this token request: Unknown

No further log entries for >30 min. No further TCP socket to the broker (TCP socket scan against the listener PID returns empty). Process alive but parked — main thread in ObjectNative::WaitTimeout -> SyncBlock::Wait -> _pthread_cond_wait per sample <pid> 3.

All four listeners on the host hit the same final-log signature within a 32-minute window (08:11-08:43 UTC), suggesting an upstream broker-session event (TLS keepalive teardown, broker rolling restart, or scheduled session expiry policy) triggered the wedge — but the runner client failed to re-establish.

Reproduction

Not deterministically reproducible from a fresh runner, but reliably observed in the steady-state of long-lived self-hosted runners. The trigger appears to require:

  • Listener lifetime > a few hours (so credential refresh happens)
  • A broker disconnect concurrent with or immediately following an AAD credential refresh
  • macOS Apple Silicon (we have not observed it on Linux x64)

Workaround

Out-of-band watchdog that probes for ESTABLISHED TCP on the listener PID and the freshness of _diag/Runner_*.log, and launchctl bootout/bootstrap if both fail. Reference implementation: https://github.com/EVCA-Org/evca-web-app/pull/16130

Asks

  1. Detect the silent-exit path and either exit non-zero (so a supervisor restarts) or keep retrying loudly.
  2. After regaining the broker session, re-sync agent state so any in-flight job that was acknowledging at the moment of disconnect is correctly transitioned out of "busy" on the broker side.
  3. If there is already an internal channel for the "Correlation ID: Unknown" AAD response, surface it in the diag log instead of swallowing it.

Full diag logs for all four affected listeners available if helpful for diagnosis — happy to attach.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing Runner.Listener's broker-reconnect handling around the AAD credential-refresh log sequence, using the reported socket and pthread_cond_wait symptoms to identify where progress stops. Compare the full diag logs from the four runners and test the long-lived macOS reconnect scenario. Done means the listener either retries with visible diagnostics or exits non-zero, and broker agent state is re-synchronized after recovery.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, macos
Domain
ci-cd, devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.