GitHub Actions: "The job has exceeded the maximum execution time"
The job ran longer than its time limit and GitHub cancelled it. The limit is the job's timeout-minutes (360 if you set none) or the runner's own limit, whichever comes first. Before raising it, find out which step was running and whether the job was slow or stuck.
What it looks like
The annotation gives the limit as a duration: timeout-minutes: 20 shows as 20m0s, 90 as 1h30m0s, and the default 360 as 6h0m0s. From a public job on November 2, 2026:
The job has exceeded the maximum execution time of 20m0s
Older runs and many write-ups show an earlier form, "The job running on runner NAME has exceeded the maximum execution time of 360 minutes".
- The job ends as Cancelled, not Failure. The step that was running stays "in progress", and the steps after it, post steps included, never start.
- A later step with
if: ${{ !cancelled() }}does not run either, because the job counts as cancelled: in that job, its JUnit upload never started (gh-widgets #286). - Jobs that need it, such as an "all checks passed" gate, fail (noter #94), and in a merge queue the pull request is removed from the queue (freenet-core #6095, below).
Which limit it was
- The job's
timeout-minutes: default 360. The job is cancelled with the message above. - A step's
timeout-minutes(at most 360): only that step is stopped, and it fails with "The action 'NAME' has timed out after 25 minutes." The job fails rather than being cancelled, and later steps withif: always()still run: in a public job on November 1, 2026, an artifact upload ran after the timed-out step, and so did the post steps (Silinx-ISE #2462). - The runner's limit: 6 hours per job on GitHub-hosted runners, 5 days on self-hosted ones. A larger
timeout-minutesdoes not help; the job is cancelled at the runner's limit. - The whole workflow run: 35 days, waiting and approvals included.
- On self-hosted runners,
GITHUB_TOKENexpires after at most 24 hours, so a longer job can lose access before its timeout.
A job whose steps all passed
Two projects reported this on November 2, 2026. In one, every step of a formatting job passed, Complete job included, by 3m40s; the last log line was Cleaning up orphan processes, and about two and a half minutes later the job was cancelled with "The job has exceeded the maximum execution time of 5m0s" (freenet-core #6095). In the other, a merge-queue job finished its steps in about 4m40s and was cancelled at 7 minutes, in 3 of 30 runs (claude-code-plugins #12506). Neither cause is known yet; both reports name a process left behind by a step as one candidate.
- List what is still running in a last step before you change the limit:
ps -efon Linux and macOS. - A step with
background: trueis waited for before post-job cleanup (an implicitwait-all), so a server started that way keeps the job open until it ends or is stopped. Stop it with acancelstep:
steps:
- name: Start the server
id: server
run: ./scripts/serve.sh
background: true
- name: Run the tests
run: npm test
- name: Stop the server
cancel: server
What to do
- Find the step that was running when the job was cancelled:
gh api repos/OWNER/REPO/actions/jobs/JOB_ID --jq '.steps[] | select(.status != "completed") | .name'
- Compare with recent successful runs before raising the limit. One project's job took 237 seconds at the median and 282 at the slowest against a 300-second limit, so ordinary differences between runners could be enough to cancel it (freenet-core #6095).
- Put a
timeout-minuteson the step that can run long, below the job's: a stuck step then fails on its own, with its name in the message, and uploads withif: always()still run. - Split long work into more matrix jobs or shards. In noter #94 one Windows shard took 63 minutes on the previous run and ran past 90 on the next, while the other Windows shards took 33 to 65.
- If it was a slow runner once, Re-run failed jobs (or
gh run rerun <run-id> --failed). - For work that needs more than 6 hours, use a self-hosted runner.
Other cancelled jobs
- No runner ever took the job: cancelled after about 15 minutes with no steps ("The job was not acquired by Runner of type hosted").
- A newer run in the same concurrency group: "Canceling since a higher priority waiting request" (cancelled checks on a pull request).
- The runner itself stopped: the job fails with "The runner has received a shutdown signal", and even
if: always()steps are skipped (what to do). - Someone cancelled the run, or it would not stop: when a run will not stop, and force-cancel.
See cancelled jobs and their steps from IntelliJ IDEA
Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, so a cancelled job stands out next to the ones that passed, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs, cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).
GitHub and GitHub Actions are trademarks of GitHub, Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc. or JetBrains. Limits are from GitHub's documentation on November 3, 2026.
More: "The job was not acquired by Runner of type hosted" · "Waiting for a runner to pick up this job" · Re-running jobs with debug logging · GitHub Actions plugins for IntelliJ IDEA compared · 日本語