Problem
A workflow that had run successfully began failing in JavaScript-based action steps after September 23, 2026. Re-adding this environment variable did not help:
env:
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: "true"Application commands were not the first failure. Checkout, setup, cache, or another uses: step failed before the main build ran.
Root Cause
GitHub removed Node 20 from Actions runners on September 23, 2026. JavaScript actions now run with Node 24, and the temporary Node 20 opt-out is no longer available.
The workflow had two stale dependencies:
- an old action release built for the retired runtime;
- a self-hosted runner on a host that was not compatible with Node 24.
Node 24 is incompatible with macOS 13.4 and earlier and does not officially support ARM32. Installing a different Node version with actions/setup-node does not change the runner's internal JavaScript-action runtime.
Confirm Which Action Fails
Expand the first failed uses: step in the job log. Then inventory every action reference:
rg -n "uses:" .github/workflows .github/actionsFor a local JavaScript action, inspect its metadata:
rg -n "using:|main:|post:" .github/actions/**/action.y*mlFor self-hosted runners, record the runner version, operating system release, and architecture. A workflow label such as self-hosted does not prove the host supports Node 24.
Fix
First, upgrade external actions to reviewed releases that support Node 24:
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npmThe application can still use Node 22 in this example. Node 24 is the runtime for the action implementation, not automatically the application.
For an internal JavaScript action, update action.yml and rebuild its distributable bundle:
runs:
using: node24
main: dist/index.jsnpm ci
npm test
npm run build
git diff --checkNext, update the self-hosted runner using GitHub's supported runner upgrade process. If the host is macOS 13.4 or older or ARM32, move the workload to a supported operating system and architecture. Do not copy Node 20 back into the runner directory as a permanent workaround.
Finally, delete the obsolete opt-out:
# Remove this; it stopped working after Node 20 removal.
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: "true"Verify the Entire Job
Run the workflow on a non-production branch and check more than the main build step:
- checkout and setup steps complete;
- cache restore and cache save both work;
- artifacts upload successfully;
- authentication cleanup and other post-job hooks complete;
- the same workflow works on each self-hosted runner group;
- no logs still mention the retired Node 20 opt-out.
If a third-party action has no Node 24-compatible release, replace it with a maintained alternative, fork it under your organization's review process, or temporarily implement the required operation with a well-scoped shell step. Do not switch to an unreviewed branch merely to make the run green.
Lesson
actions/setup-node controls the Node version used by your project. The runner controls the Node version used to execute JavaScript actions. When a uses: step fails after a runner runtime retirement, update the action and runner platform first.
For the complete inventory and rollout process, see the GitHub Actions Node 24 migration guide.