GitHub Actions: cancel the Sume Format run when the job is canceled

A canceled GitHub job does not stop a Sume run. Add a step with if: cancelled() that posts to the run's cancel URL, within GitHub's 7.5 second signal window.

5 min readSume
All posts

Canceling a GitHub Actions workflow stops your runner, not the Sume run it started. To stop the Sume side too, add a later step with if: cancelled() that posts to POST /v1/format-runs/{run_id}/cancel. GitHub re-evaluates step conditions on cancellation and keeps running steps whose condition is true, so the cleanup step runs.

The GitHub facts are from Workflow cancellation and the expressions reference, both read 2026-10-10. The Sume facts are from Runs and results.

What happens on cancel?

The page describes the sequence. Step if conditions are re-evaluated, and steps whose condition is true keep running. The running step receives SIGINT; after 7500 ms it gets SIGTERM, and after another 2500 ms it is killed. The server force-terminates the workflow after a 5 minute cancellation timeout. The expressions page says cancelled() is true when the workflow was canceled, always() runs even when canceled, and !cancelled() is the recommended alternative to always().

The timing is worth remembering: from the first signal to the kill of the running step is 7.5 plus 2.5, which is 10 seconds. A poll loop in bash should trap SIGINT or SIGTERM and exit quickly, so the cleanup step starts without waiting out the force-terminate timeout of 5 minutes.

Cancellation timeline (GitHub docs, read 2026-10-10)
MomentWhat GitHub doesWhat to do with Sume
Cancel requestedRe-evaluates step conditions; true ones keep runningA step with if: cancelled() is scheduled
Running stepSIGINT, then SIGTERM after 7500 ms, then kill after 2500 msDo not rely on the poll step to clean up
Cleanup stepRuns if its condition is truePosts the Sume cancel
5 minutesThe server force-terminatesKeep the cleanup step short

What does the cancel call do?

Per the runs page, POST /v1/format-runs/{run_id}/cancel needs the formats:write scope, is idempotent, and returns cancel_effect of canceled or no_op. A cancel at any point is accepted, and you pay for the generation completed so far. If the run already ended, the result is no_op, which is harmless.

That makes it safe to call from a cleanup step even when you do not know the state. The call does not need the run to be in progress.

How do I wire it?

The submit step must save the run id where later steps can read it. Writing it to $GITHUB_ENV works for steps in the same job. The cleanup step reads the variable; if the submit never happened, the variable is empty and the step should do nothing.

Put the cleanup step last and give it a condition on cancellation only. Use cancelled() rather than always() so a normal failure does not cancel a run you may want to inspect.

Test it. Start a run in a scratch repository, cancel the workflow from the Actions page, then read the run by id. You should see a canceled status and a cancel_effect in the response of the cleanup step.

      - name: Submit
        run: |
          ID=$(curl -sS -X POST https://api.sume.com/v1/formats/acme/promo/runs \
            -H "Authorization: Bearer $SUME_API_KEY" -H "Content-Type: application/json" \
            -H "Idempotency-Key: gh-${{ github.run_id }}" \
            -d '{"input":{"sku":"A-100"}}' | jq -r .data.id)
          echo "SUME_RUN=$ID" >> "$GITHUB_ENV"
      - name: Cancel the Sume run
        if: cancelled()
        run: |
          [ -n "$SUME_RUN" ] && curl -sS -X POST "https://api.sume.com/v1/format-runs/$SUME_RUN/cancel" \
            -H "Authorization: Bearer $SUME_API_KEY"

What are the limits of this?

The step runs only if the runner still has a connection and the key is still available to the job. If the environment holds the key behind a reviewer gate, the secret was already given to the job at start, so the cleanup step has it. A runner that is lost does not run the step, so also set a budget on the request. The spend cap bounds the loss, and the run's own deadline force-finalizes it at 90 minutes.

This applies to Format runs. Plain jobs behave differently: a job cancel works only before generation starts, and later returns 409 job_generation_already_started.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume