Your GitHub Actions runs are getting cancelled because the system decided they’d gone on too long or too many were running at once.

Common Causes and Fixes

1. Concurrency Limit Reached

  • Diagnosis: Check the "Concurrency" section of your workflow run. You’ll see a message like "This run was cancelled because a new run triggered for the same job or workflow on a different ref." This indicates you’ve hit the concurrency limit for that specific workflow or job.

  • Cause: GitHub Actions has a default concurrency limit to prevent runaway jobs and excessive resource usage. If you trigger multiple runs of the same workflow rapidly (e.g., through frequent commits to the same branch, or multiple branches being pushed), you can exceed this limit.

  • Fix: Increase the concurrency limit for your workflow. In your workflow .yml file, add or modify the concurrency block.

    name: CI
    
    on: [push]
    
    concurrency:
    
      group: ${{ github.workflow }}-${{ github.ref }}
    
      cancel-in-progress: true # This is the default, but explicitly setting it is good practice.
    

    To increase the limit, you’d typically adjust the group or use a different identifier if you want more concurrent runs for the same group. However, the more common scenario is that you want to prevent cancellation due to concurrency. The cancel-in-progress: true setting is what cancels the older run when a new one starts for the same group. If you want to allow multiple runs for the same group, you’d need to remove the cancel-in-progress: true or adjust the group identifier so that subsequent runs don’t fall into the same group.

    A more common and often correct approach is to ensure your group is specific enough to avoid unintended cancellations. For instance, if you want to limit concurrency per branch, the default group: ${{ github.workflow }}-${{ github.ref }} is usually correct. If you want to limit concurrency globally for a workflow, you’d use group: ${{ github.workflow }}.

    The actual fix for preventing cancellation due to concurrency is often to ensure your concurrency group is well-defined and that you don’t have more jobs starting than your desired limit allows. If you see cancellations, it means the newer job is cancelling the older one because cancel-in-progress is true. To allow all jobs to run (even if it means many at once), you’d either:

    • Remove cancel-in-progress: true if you’re okay with potentially many jobs running simultaneously and not cancelling each other.
    • Or, more practically, ensure your group identifier is more granular if you want to allow multiple runs within a broader scope but limit them within a tighter scope (e.g., allow multiple pushes to different branches to run, but only one run per specific branch).

    If you are seeing cancellations and you want the newer run to not cancel the older one, then you need to ensure that the group identifier is different for the runs you want to run concurrently. For example, if you’re pushing to main multiple times very quickly, and each push triggers a workflow, they will all share the same group: "CI-main" and the newer ones will cancel the older ones. If you don’t want them to cancel, you’d need a more dynamic group identifier, which is rarely the desired behavior. The typical reason for cancellation is hitting the limit, and the fix is to understand why you’re hitting it and adjust your workflow triggers or concurrency group.

  • Why it works: The concurrency setting allows you to define a group of jobs that should run exclusively. When a new job starts for a group where cancel-in-progress is true and the group is already running jobs, GitHub Actions cancels the existing job(s) in that group to make way for the new one. By carefully defining the group and understanding cancel-in-progress, you control this behavior.

2. Workflow Timeout Reached

  • Diagnosis: Look at the workflow run summary. You’ll see a status indicating "Cancelled" and often a timestamp showing when it was cancelled, suggesting it ran for a long duration. The job logs themselves might also have a message indicating it was cancelled due to exceeding the maximum runtime.

  • Cause: GitHub Actions jobs have a maximum runtime. For self-hosted runners, this is unlimited. For GitHub-hosted runners, it’s 360 minutes (6 hours) for public repositories and 3600 minutes (60 hours) for private repositories. If a job exceeds this, it’s automatically cancelled.

  • Fix: Optimize your workflow to run faster, or if your workflow legitimately needs more time, consider migrating to self-hosted runners. For GitHub-hosted runners, ensure your build/test/deploy steps are efficient. This might involve:

    • Parallelizing tests.
    • Caching dependencies effectively.
    • Reducing the amount of work done per run (e.g., only building changed components).
    • For private repos, ensuring you’re not hitting the 60-hour limit. If you are, that’s an extreme case and self-hosted runners are strongly recommended.
    jobs:
      build:
        runs-on: ubuntu-latest
        timeout-minutes: 60 # Example: Set a specific timeout for this job (optional, defaults apply)
        steps:
          - uses: actions/checkout@v3
          # ... your steps ...
    

    Note: Setting timeout-minutes to a higher value than the default (e.g., timeout-minutes: 360 for public, timeout-minutes: 3600 for private) does not bypass the system limits. It only allows you to set a lower limit if you wish.

  • Why it works: By optimizing your workflow, you reduce the execution time. If the task genuinely requires more time than the GitHub-hosted runner limits, migrating to self-hosted runners removes this constraint entirely.

3. Manual Cancellation

  • Diagnosis: Check the "Summary" tab of the workflow run in GitHub. Look for who cancelled the run. If it says "Cancelled by [username]" or "Cancelled by GitHub Actions," it might have been a manual intervention. Also, check the "Actions" tab for any "Cancel workflow run" buttons that might have been clicked accidentally.
  • Cause: Someone with write access to the repository can manually cancel a running workflow from the GitHub UI. This can happen accidentally or intentionally if a run is deemed unnecessary or problematic.
  • Fix: Be mindful of who has write access and the "Cancel workflow run" button. Train team members on its use. If you’re automating cancellation (e.g., via a webhook or another workflow), ensure that logic is correct. There’s no configuration to prevent manual cancellation by authorized users, as it’s a core feature.
  • Why it works: This isn’t a "fix" in the sense of changing system behavior, but rather a procedural one. Understanding that manual cancellation is possible helps prevent accidental triggers.

4. GitHub Platform Issues

  • Diagnosis: Check the GitHub Status page (https://www.githubstatus.com/). If there are active incidents related to Actions or runner availability, this could be the cause. Look for messages about "Actions runner degradation" or "workflow cancellations."
  • Cause: Occasionally, GitHub’s own infrastructure can experience issues that lead to unexpected workflow cancellations. This is rare but possible.
  • Fix: There is no fix you can apply. Wait for GitHub to resolve the issue. Keep an eye on the status page.
  • Why it works: This is a workaround. You can’t fix an external system failure, only wait for it to be resolved.

5. Incorrect Workflow Trigger Logic

  • Diagnosis: Review your .github/workflows/*.yml files. Examine the on: section. Are you accidentally triggering workflows too frequently or in unintended ways? For example, a push to main might trigger a workflow, and then a subsequent pull_request to main from a fork might trigger another, potentially leading to concurrency issues or unexpected cancellations if not managed.

  • Cause: Complex or overlapping trigger conditions can lead to multiple workflow runs starting simultaneously, especially if they share the same concurrency group.

  • Fix: Refine your on: triggers. Be precise about which events should start which workflows. For example, use on: push: branches: [main] instead of a broad on: push if you only want pushes to main to trigger a specific workflow. If you have multiple workflows that could run on the same event, ensure their concurrency groups are distinct or that you have a clear strategy for which one should win (e.g., using cancel-in-progress).

    on:
      push:
        branches:
          - main
          - develop
      pull_request:
        branches:
          - main
    

    In this example, both push and pull_request events targeting main would use the default concurrency group workflow_name-main. If you want them to run independently, you’d need to assign different concurrency groups.

  • Why it works: By making your triggers more specific, you reduce the likelihood of multiple, unintended workflow runs starting concurrently, thus avoiding concurrency limits and potential cancellations.

The next error you might encounter after resolving these is related to specific job failures within a workflow that is running, such as "Job failed" or "Step X failed."

Want structured learning?

Take the full Github-actions course →