GitHub Actions concurrency for a Sume render job: the cancel trap

cancel-in-progress stops your workflow, not the Sume job it submitted. Group by branch, cancel via POST /v1/jobs/{id}/cancel in a final step, and cap the run.

4 min readSume
All posts

Put the render job in a concurrency group keyed by branch, but know that cancel-in-progress: true only stops your workflow. The Sume job it already submitted keeps going unless a step calls POST /v1/jobs/{id}/cancel, and Sume accepts that cancel only before generation starts.

So the safe setup has three parts: a group, a deliberate choice about cancellation, and a step that records the job id so something can cancel it.

What GitHub's concurrency does

The concurrency docs (read 2026-10-10) say that only one run in a group runs at a time, and that while one is running only one other can be pending, so a newer pending run replaces the older pending one. cancel-in-progress: true instead cancels the running one. The workflow syntax reference (read 2026-10-10) adds a queue: max option, which lets up to 100 runs wait, and says that combining queue: max with cancel-in-progress is a validation error.

Choices for a paid render (GitHub docs read 2026-10-10)
SettingEffect on the workflowEffect on the Sume job
Default, no cancelOne run, one pending, older pending droppedEach started run finishes its job
cancel-in-progress: trueRunning workflow is stoppedJob keeps running unless you cancel it
queue: maxUp to 100 runs wait their turnOne render at a time, no duplicates

A workflow that serializes renders

This file has a group per branch, no cancel-in-progress, a 30-minute workflow timeout, and a stable Idempotency-Key built from the run id. It submits and stores the job id as a step output. If you do enable cancellation, add a cleanup step that calls POST /v1/jobs/{id}/cancel with that output; check the step-condition rules in the workflow syntax reference for when such a step runs, and remember that Sume accepts the cancel only while generation has not started.

on: workflow_dispatch
concurrency:
  group: sume-render-${{ github.ref }}
  cancel-in-progress: false
jobs:
  render:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    env:
      SUME_API_KEY: ${{ secrets.SUME_API_KEY }}
    steps:
      - id: submit
        run: |
          id=$(curl -sS https://api.sume.com/v1/image-1.0/generate \
            -H "x-api-key: $SUME_API_KEY" \
            -H "Idempotency-Key: ci-${{ github.run_id }}-1" \
            -H "Content-Type: application/json" \
            -d '{"prompt":"Matte bottle on marble","mode":"async"}' \
            | jq -r '.data.job_id')
          echo "job=$id" >> "$GITHUB_OUTPUT"

Choosing the group key

Group on what you want to serialize. github.ref serializes per branch, which is right for a preview render. A fixed string serializes the whole repository, which matches a small plan's concurrency limit but delays unrelated pull requests. Sume's own admission queue already absorbs bursts: a workspace at its concurrency limit can still accept jobs as queued while capacity remains, and fails with 429 queue_full only past it. That means the group key protects your budget and your queue, not Sume's servers.

Use the secret from repository secrets as shown, never echo it, and scope the key to what the job needs.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume