Skip to content
Latchkey

GitHub Actions success() and failure() both false when the run is cancelled

success() is true only when prior steps succeeded; failure() is true only when one failed. A cancelled run is neither, so a step guarded by success() || failure() is skipped - cleanup that should always run does not.

What this error means

A cleanup or teardown step is skipped when a job is cancelled, even though it should run regardless of outcome.

github-actions
- name: Teardown
  if: ${{ success() || failure() }}   # both false on cancel -> step skipped
  run: ./teardown.sh

Common causes

cancelled() is a distinct state

A cancel is neither success nor failure; cancelled() must be checked separately or always() used.

Misuse of success() || failure() as a catch-all

That combination excludes cancellation; it is not equivalent to always().

How to fix it

Use always() for unconditional cleanup

  1. Replace the success/failure combination with always() so the step runs in every outcome including cancellation.
  2. Inside the step, branch on the specific status if needed.
.github/workflows/ci.yml
- name: Teardown
  if: ${{ always() }}
  run: ./teardown.sh

Explicitly include cancelled()

  1. If you do not want always(), add || cancelled() to cover the cancel state.
  2. Combine the three status functions for the exact set you intend.

How to prevent it

  • Use always() for teardown that must run on cancellation.
  • Remember cancelled() is separate from success() and failure().

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →