timeline_get 409 job_not_completed on Sume MCP: retry, do not give up
A 409 job_not_completed from timeline_get means the render is still running, so retry. Call jobs_wait on the same job_id; never report the run blocked.

On Sume's hosted MCP, timeline_get returning 409 job_not_completed means the render is still running. It is retryable and never terminal: call jobs_wait on the same job_id with timeout_seconds between 45 and 55, and repeat until the job is terminal.
What the next steps say
The tool's built-in next steps are explicit. First, 409 job_not_completed means still rendering, retryable, never terminal, and an agent must never report blocked on it. Second, do not hand-poll; use jobs_wait on the same job and repeat that wait until the job is terminal or you have waited at least ten minutes in total. Third, deliver video_url and treat structured warnings[] as soft degradations, not hard failures.
| Observed | Meaning | Action |
|---|---|---|
| 409 job_not_completed | still rendering | jobs_wait on the same job_id |
| wait slice with timed_out true | your window closed, job continues | re-issue jobs_wait |
| success with warnings[] | soft degradation | deliver video_url, mention warnings |
| status failed | terminal | read jobs_get, then see the recovery code |
Do not mix the two 409s
jobs_result also answers 409 job_not_completed, but on a failed job it means something else: the job is over and has no result, so read jobs_get. For timeline_get the same status on a running job means wait. Decide using the job status, not the HTTP code alone. The jobs_wait post covers slice sizes.
A loop that follows the contract
The sketch below uses a stand-in for the wait call so it runs as written.
import time
def wait_until_done(wait, job_id, budget_s=600, slice_s=50):
start = time.monotonic()
while time.monotonic() - start < budget_s:
res = wait(job_id, slice_s)
if res.get('terminal'):
return res
raise TimeoutError('still rendering after budget; do not report blocked')
fake = iter([{'terminal': False}, {'terminal': True, 'status': 'completed'}])
print(wait_until_done(lambda j, s: next(fake), 'job_1', budget_s=5))Limits
Ten minutes is the contract's minimum wait before escalating, not a promise about render time. If the budget passes, tell the user the render is still running and offer to check again; that is accurate, whereas claiming failure is not.
Why the long tail matters
Most renders finish quickly, but the tail is the case that matters. A render that had to retry is also the one carrying work nobody wants to pay for twice. The server's own comment records an incident where two polls 90 seconds apart both returned 409, the run was declared blocked, and the render finished 80 seconds later. A finished deliverable sat unused for a day.
Checklist before you ship
- Treat 409 job_not_completed on timeline_get as still rendering.
- Wait with jobs_wait on the same job_id at 45 to 55 seconds per slice.
- Keep waiting for at least ten minutes in total before escalating.
- Deliver video_url and surface warnings as soft degradations.
Sources
Related posts
More in Developers
- Sume MCP tools_schema safety object: build a tool allowlist from it
Sume's tools_schema returns a safety object per tool: paid_generation, read_only, requires_idempotency_key and more. Build your agent allowlist from it.
- Why a Sume output schema is rejected: the strict subset rules
Sume saves an output schema only in the strict subset: object root, additionalProperties false, all properties required, nullable unions, 10 levels.
- Sume queue position and ETA: none exists, poll generation_limits
Sume shows queue counts and remaining capacity, not a per-job position or ETA. Read generation_limits and keep new work inside the headroom formula.
- Sume schedule invoke command: check the host before you paste it
The curl command on a Sume schedule's trigger card uses api.dev.sume.com when you copy it from a *.dev.sume.com dashboard. Check the host before production.
Written by Sume