CircleCI no_output_timeout: poll a Sume video job without a kill
CircleCI kills a run step after 10 silent minutes. Print a status line on every poll, raise no_output_timeout, and keep one Sume Idempotency-Key per workflow.

A CircleCI run step stops when it prints nothing for 10 minutes, because that is the default of no_output_timeout. A Sume video job can sit in queued longer than that on a small plan, so a quiet poll loop is killed while the job keeps running and billing. Fix it two ways: print one line on every poll, and set no_output_timeout above your longest expected wait. Then derive the Sume Idempotency-Key from the CircleCI workflow id, so a rerun of the job never pays twice.
This post reflects CircleCI's configuration reference and Sume's jobs and admission docs, all read on 2026-10-11.
What does no_output_timeout do?
CircleCI describes it as the elapsed time a command can run without output. The default is 10 minutes, and the value is a string with a unit suffix such as 20m or 5s. The overall job limit is separate and depends on the plan.
A poll loop that only prints on completion looks idle to CircleCI. A loop that echoes the status each time resets the clock, so the echo is the real fix and the longer timeout is the safety margin.
| Limit | Value | Source |
|---|---|---|
| no_output_timeout default | 10 minutes | CircleCI configuration reference |
| Maximum job runtime, Free plan | 1 hour | CircleCI configuration reference |
| Maximum job runtime, Performance plan | 3 hours | CircleCI configuration reference |
| Maximum job runtime, Scale plan | 5 hours | CircleCI configuration reference |
Why can a Sume job wait that long?
Sume admits paid generation queue-first. When your workspace is at its concurrency limit, a valid submit still returns a job in queued, and workers move it to processing when a slot opens. queued is a normal state, not a failure. Statuses completed, failed and canceled are terminal.
Sume's docs say to poll with backoff and not to submit the original paid request again just because a local process timed out. That second rule is why the key below matters.
What does the step look like?
The step submits once with an Idempotency-Key built from CIRCLE_WORKFLOW_ID, then polls GET /v1/jobs/{id}/status. A /v1/videos response carries the job id in id. The exit codes let the next step tell success (0), a terminal failure (1) and a give-up (2) apart.
version: 2.1
jobs:
render:
docker: [{image: cimg/base:stable}]
steps:
- run:
name: Submit once, poll until terminal
no_output_timeout: 20m
command: |
set -euo pipefail
KEY="build-${CIRCLE_WORKFLOW_ID}-promo"
ID=$(curl -fsS https://api.sume.com/v1/videos \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"model":"sume/auto","prompt":"Product spin, studio light"}' \
| jq -r .id)
for i in $(seq 1 120); do
S=$(curl -fsS "https://api.sume.com/v1/jobs/$ID/status" \
-H "Authorization: Bearer $SUME_API_KEY" | jq -r .status)
echo "job $ID: $S"
case "$S" in
completed) exit 0 ;;
failed|canceled) exit 1 ;;
esac
sleep $(( i < 6 ? 5 * i : 30 ))
done
exit 2What happens when the job is rerun?
Rerunning a workflow from failed keeps the same workflow id, so the same key goes out with the same body. Sume treats that as an exact retry. Reusing a key with a different body returns 409 idempotency_conflict, so change the key suffix when you change the prompt.
If the loop ends with exit code 2, the job is still alive on Sume. Read its status again rather than submitting a new one; a cancelled CI run does not cancel the Sume job, as covered in GitHub Actions cancel-in-progress and Sume jobs.
Should CI poll at all?
For a single clip, polling is simple and fine. For a batch, a webhook receiver avoids holding a runner for minutes. A shell version of the loop with its own exit codes is in Poll a Sume job from a CI step with curl and jq.
Sources
Related posts
More in Developers
- Clojure: Ring middleware that verifies a Sume webhook from raw bytes
Verify a Sume webhook in Clojure: slurp Ring's one-shot body as bytes, HMAC-SHA256 with javax.crypto, MessageDigest/isEqual, rotation entries, 300 s window.
- Django view for Sume webhooks: verify request.body, then parse
A Django view that verifies a Sume webhook against request.body: csrf_exempt, 300 s replay window, empty-secret refusal, multi-signature header, 204.
- Eleven rows on Sume return MP3 only: use Sonic when you need WAV
Sume's eleven-* TTS Router ids return MP3 only, and three of the five accept just one format. WAV, avatar voices and word timings belong to the sonic ids.
- Eleven v4 seed and voice settings: what each Sume router id accepts
Which of stability, similarity_boost, style, speed and seed work on eleven-v4, v4-turbo, v3, multilingual-v2 and turbo-v2.5 in the Sume TTS Router, with ranges.
Written by Sume