Skip to main content
DevOps Insightsguides & answers

GitHub Actions · troubleshooting

Runner version is deprecated and cannot receive messages

GitHub stops sending jobs to a self-hosted runner that is too far behind the newest runner release, and GitHub Enterprise Cloud enforces this since 29 Sep 2026. A runner must be updated within 30 days of each release, and at once for critical security fixes. The runner prints this line and exits. The fix is to run a current runner version (v2.338.0 on 21 Oct 2026), usually by letting it update itself or by bumping the version baked into your image.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 10 sections

Before you start

  • Shell access to a runner machine, or kubectl access to the runner pods if you use Actions Runner Controller.
  • The runner's log or container output from its start-up.
  • For the API checks: the gh CLI or a token with admin access to the repository or organization.

Run this first

step 1 of 3
./bin/Runner.Listener --version

Look for Compare with the latest release on github.com/actions/runner/releases

Run in the runner's install directory: the version the runner reports to GitHub.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

Is the version in the message the newest runner release?

Cause space

3 of 3 still possible

  • Automatic updates are off and the runner fell behindCommon
  • Every new runner starts from an image with an old version insideCommon
  • You are on the newest release and it is still refusedRare

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Check it with a tool

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    ./bin/Runner.Listener --version

    Run in the runner's install directory: the version the runner reports to GitHub.

    Look for Compare with the latest release on github.com/actions/runner/releases

  2. Step 2

    gh api orgs/ORG/actions/runners --paginate --jq '.runners[] | [.name, .version, .status] | @tsv'

    Every self-hosted runner in the organization with its version, so you find all the old ones, not just the one you noticed.

    Look for Versions older than the latest release, and runners shown offline

  3. Step 3

    gh api orgs/ORG/actions/runners/deprecations/2.330.0

    Since 3 Sep 2026 GitHub answers, per version, when registration and runtime support end. The repository-level path is repos/OWNER/REPO/actions/runners/deprecations/VERSION.

    Look for registration_deprecates_at and runtime_deprecates_at

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

Automatic updates are off and the runner fell behind

Common

The runner was registered with --disableupdate, or a community runner image turned updates off for you. Updates are then yours to install, within 30 days of every release, and the deadline passed.

Confirm

cd ~/actions-runner   # the runner's install directory
./bin/Runner.Listener --version
grep -i disableupdate .runner

A version older than the latest release, and disableUpdate set to true

Fix

  • Install the current release from github.com/actions/runner/releases, following its install instructions, and check the SHA-256 the release lists.
  • If nothing requires updates to be off, re-register the runner without --disableupdate so it keeps itself current.
  • If updates must stay off (container images, change control), make the update a scheduled job: watch the actions/runner releases and ship each one well inside 30 days.

Every new runner starts from an image with an old version inside

Common

Ephemeral runners in containers or VMs start from the image each time: an Actions Runner Controller template pinned to ghcr.io/actions/actions-runner:2.330.0, a Dockerfile with ARG RUNNER_VERSION=2.330.0, or a VM image built months ago. An update one runner installed is lost when it is replaced. Restarting does not help; the image has to change.

Confirm

kubectl get pods -n RUNNER_NAMESPACE -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
grep -rnE 'actions-runner:|RUNNER_VERSION' .

An actions-runner tag or RUNNER_VERSION older than the latest release

Fix

  • Bump the image tag to the current release, for example ghcr.io/actions/actions-runner:2.338.0 (the tags have no leading v), or RUNNER_VERSION in your own Dockerfile, rebuild and roll the runners.
  • Using :latest avoids falling behind but makes every pod start pull whatever is newest. Pinning a version plus an automated bump (Renovate, Dependabot) keeps both control and currency.
  • For VM images, rebuild them on a schedule shorter than 30 days.
image: ghcr.io/actions/actions-runner:2.338.0

In the ARC runner template's containers; check the releases page for a newer version first.

You are on the newest release and it is still refused

Rare

On 6 Aug 2026 GitHub refused the latest release, v2.336.0, with this message during an incident, and fixed it the same day without any change by users. If your version matches the newest release, the problem is not yours to fix.

Confirm

./bin/Runner.Listener --version
curl -s https://api.github.com/repos/actions/runner/releases/latest | grep '"tag_name"'

The two versions match

Fix

  • Check githubstatus.com for an Actions incident and wait for it to be resolved.
  • Do not downgrade or re-register runners in a hurry; the version you have is the one GitHub will accept again.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

The program that runs your jobs on your own machines, the Actions runner, is updated every few weeks. GitHub only talks to recent versions. If yours is older than GitHub accepts, it is refused when it asks for work, prints this message and stops, and your jobs sit in the queue waiting for a runner that never comes. Normally the runner updates itself, so this happens when updates were switched off, or when every new runner starts from a machine image or container image that still has an old version inside.

Words used on this page

Self-hosted runner
A machine you run that executes GitHub Actions jobs, as opposed to the GitHub-hosted ones.
Automatic update
By default a runner downloads and installs each new runner release itself. Registering with --disableupdate turns that off.
Registration minimum
The oldest version that may register as a new runner: 2.329.0 under GitHub's 2026 enforcement.
Runtime minimum
The oldest version that may still receive jobs. It is higher than the registration minimum and moves with each release.
ARC
Actions Runner Controller: runs runners as Kubernetes pods, usually from the ghcr.io/actions/actions-runner image.
What is actually happening

GitHub's self-hosted runner reference says that with automatic updates off you must install each runner release within 30 days, and that otherwise the GitHub Actions service will not queue jobs to your runner; a critical security update has to be applied before jobs are queued again.

In 2026 GitHub began enforcing this on a schedule: GitHub Enterprise Cloud with data residency from 31 Jul 2026, and GitHub Enterprise Cloud from 29 Sep 2026 (moved from an earlier announced date). Runners below 2.329.0 cannot register or re-register; runners below the runtime minimum stop running jobs even if registered. GitHub says GitHub Enterprise Server is not affected by that change. Users of Enterprise Server have reported the same message after upgrading the server, so check your server's release notes for its minimum.

The message comes from GitHub, not from your machine: the runner receives an access-denied answer and prints An error occurred: followed by it. By default the runner then reports a terminated error and run.sh exits with code 0, so a container or service manager sees a clean exit, does not raise an alarm, and may not restart it. That is why this often shows up as jobs stuck on Waiting for a runner to pick up this job rather than as an error.

Since v2.333.0, setting ACTIONS_RUNNER_RETURN_VERSION_DEPRECATED_EXIT_CODE=1 makes run.sh exit with code 7 for this case instead, so your orchestration can tell an outdated runner from a normal stop.

On 6 Aug 2026 the then-newest release, v2.336.0, was refused with this message for some hours during a GitHub incident (actions/runner#4613). If you are already on the latest release, it is not your runner.

How to tell this is your problem
WhereWhat you see
Runner output on start-upAn error occurred: Runner version v2.330.0 is deprecated and cannot receive messages.
run-helper.sh after that lineRunner listener exit with terminated error, stop the service, no retry needed.
Workflow run pageJobs for self-hosted labels stay queued: Waiting for a runner to pick up this job…
Container or pod statusThe runner container exits with code 0 (Completed) straight after starting, and is replaced by another that does the same.

How to know it is actually fixed

  • The runner starts and prints Listening for Jobs instead of the deprecated message.
  • Queued jobs for its labels start, and the runner shows as Idle or Active in the repository or organization settings.
  • The deprecations endpoint shows runtime_deprecates_at in the future, or null, for the version you now run.

Stopping it happening again

  • Leave automatic updates on unless you have a reason; if you turn them off, schedule the update inside 30 days of each release.
  • Set ACTIONS_RUNNER_RETURN_VERSION_DEPRECATED_EXIT_CODE=1 on runners from v2.333.0, so an outdated runner exits with code 7 that your monitoring or autoscaler can alert on, instead of 0.
  • Alert before the deadline: a scheduled job that reads each runner's version and the deprecations endpoint, and warns when runtime_deprecates_at is a week away.
  • Read the warning annotations GitHub adds to jobs that ran on an outdated runner; they arrive before the hard stop.

Security considerations

  • Do not work around this by keeping an old runner alive: GitHub also stops queueing jobs until a critical security update is applied, so the version you are holding back may be the one with the vulnerability.
  • Take the runner from the actions/runner releases or the ghcr.io/actions/actions-runner image, not from a copy someone posted, and check the release's SHA-256 checksum, which each release lists.
  • A token used for the deprecations API needs admin access to the repository or organization. Use a short-lived one, and do not paste it into a ticket or a log.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it