409 previous_run_not_terminal: continue a Format run after it ends

Continuing a Format run with previous_run_id while the first is still running returns 409. Wait for terminal, then continue; a failed create frees its key.

4 min readSume
All posts

409 previous_run_not_terminal means the run named in previous_run_id has not finished. A continuation replays what the first run produced, so it can only start once that run is terminal. Wait for it, then send the same continuation again. Nothing ran and nothing was charged for the refused call, and a failed create releases its Idempotency-Key, so you may reuse the key you chose.

What are the four refusals on previous_run_id?

Continuing a run is how an integration retries one scene without paying for the whole run again. The call is refused four ways, and each has a different next step.

Refusals when continuing a Format run. Source: Runs and results, docs.sume.com/formats/runs, read 2026-10-03.
Status and codeMeansNext step
404 previous_run_not_foundUnknown id, or another owner's runCheck the id and the key
400 previous_run_format_mismatchThat run was created on a different FormatContinue it on the Format it started on
409 previous_run_not_terminalIt has not finishedPoll it, then call again
400 previous_run_not_resumableNothing to continue; details has previous_run_status, has_thread, artifact_countStart a fresh run

Which previous runs can be continued?

You can read it off the earlier receipt: thread_id is not null, and the run either completed or has a non-empty artifacts[]. A failed run that left work behind can be continued, and one that left nothing cannot. A continuation is a new run with a new id, its own spend cap and its own single webhook. The original never changes.

Two traps follow. Continue by naming previous_run_id, never by sending a thread id, which is 400 unknown_parameter. And bind the same output_schema on every turn: it is per run, not inherited.

Wait, then continue

waitForRun resolves for any terminal status, so it is the right gate. Then subscribeFormatRun creates the continuation. Use a new key for the continuation, since the first run's key is bound to its own receipt.

import { subscribeFormatRun, waitForRun } from "@sume-com/sdk";

export async function retryScene(client, previousRunId: string, sceneId: string) {
  const prev = await waitForRun(previousRunId, { client, family: "format" });
  if (prev.status === "canceled" || prev.status === "skipped") {
    throw new Error(`cannot continue a ${prev.status} run`);
  }
  return subscribeFormatRun({
    client,
    path: { handle: "acme", slug: "live-commerce" },
    idempotencyKey: `${previousRunId}-retry-${sceneId}`,
    body: {
      previous_run_id: previousRunId,
      instruction: "Retry the selected scene only. Keep every other scene.",
      input: { scene_id: sceneId },
      generation_spend_cap_usd: 8,
    },
  });
}

What if I want overlapping runs instead?

That is a different control. on_active_run: "reject" returns 409 format_run_in_progress when a run is already in flight on that Format, and the cause is concurrency on the Format rather than a dependency between two runs. Do not confuse the two 409s: one is solved by waiting for a specific earlier run, the other by dropping reject or waiting for any run.

What does a continuation cost and return?

A continuation is billed as its own run against its own spend cap, so set generation_spend_cap_usd for the single scene you are redoing, not for the whole show. Generation that finished on the first run is on the thread and is not regenerated, which is what makes the retry cheap. artifacts[] on the continued run is the place to confirm what the second turn actually produced.

If the first run failed with incomplete_assembly or agent_reported_failure, continuing it is the recommended recovery, because the finished clips are real. Starting a fresh run with a new key would pay for them again.

Where do I read the first run's state?

The receipt of the earlier run is the source of truth: GET /v1/format-runs/{run_id} returns it at any status, with thread_id, status, artifacts[] and previous_run_id if it was itself a continuation. Read it before you build the continuation request, and you can tell the 409 case (still running) from the 400 case (finished with nothing to continue) without sending the call at all.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume