Skip to content

Cancel a run

View as Markdown

This guide stops a run that's running or waiting, along with the agent turns inside it.

  • A run that's running or waiting, and its id. kindgi runs list lists runs, as List runs shows.
  • kindgi dev running in your pack's directory, with Kindgi 0.1's CLI.
  • For the app's half: Node 22.12 or later, Python 3.11 or later, or Java 17 or later, with Kindgi 0.1's client, and KINDGI_API_URL and KINDGI_API_TOKEN set.

A cancel stops the run wherever it is, and ends it as cancelled.

Terminal window
pnpm exec kindgi runs cancel <run-id>

<run-id> is the run's id. It prints:

{
"ok": true,
"runId": "265627c7-1ac7-4d18-a926-6da9fcc18577"
}

The run's status is now cancelled, and its journal ends with run.cancelled:

{"sequence": 6, "kind": "wait.suspended", "nodeId": "hold", "payload": {"nodeId": "hold", "tokenId": "child:d8c2c7dc-7ceb-4e4b-8986-ee75e7c9bcc2:25"}, …}
{"sequence": 7, "kind": "run.cancelled", "payload": {}, …}
Excerpt

That run was waiting for an approval. A run can be cancelled while it runs too, and cancelling a flow's run cancels the agent turns inside it. What a cancel stops has the details.

A run that already ended can't be cancelled:

Error [conflict]: Cannot cancel run: already cancelled

A cancel takes effect at once, wherever the run is:

  • No step starts after it. A flow between two steps, or in the middle of a loop, starts nothing more, and its journal records no start after run.cancelled.
  • A step running when it lands stops. An agent turn stops waiting for its model and ends cancelled. A start that was waiting for the turn (wait: true) answers within seconds with 422 agent-turn-aborted, "Agent turn cancelled".
  • A step that ends as it lands keeps its end. Its step.completed comes after run.cancelled in the journal: it did finish.

Here a flow running a loop over 2,000 orders, each answered by an agent, was cancelled twice. The first time, the turn for order A-1012 was running: the cancel stopped it, and the journal ends at the cancel:

{"sequence": 77, "kind": "step.started", "nodeId": "answer", "payload": {"input": "A-1012", …}, …}
{"sequence": 78, "kind": "run.cancelled", "payload": {}, …}
Excerpt

The second time, the turn for A-1030 finished as the cancel landed. Its end follows run.cancelled, and nothing starts after it:

{"sequence": 185, "kind": "step.started", "nodeId": "answer", "payload": {"input": "A-1030", …}, …}
{"sequence": 186, "kind": "run.cancelled", "payload": {}, …}
{"sequence": 187, "kind": "step.completed", "nodeId": "answer", "payload": {"output": {"text": "You said: \"A-1030\"", …}, …}, …}
{"sequence": 188, "kind": "edge.evaluated", "payload": {"to": "$end", "from": "answer", "edgeId": "b1", "decision": true, …}, …}
{"sequence": 189, "kind": "iteration.completed", "nodeId": "each", "payload": {…, "iteration": 30, …}, …}
Excerpt

A step that was running when the run was cancelled, such as the loop itself or A-1012's step, has no end of its own: run.cancelled ends it, as it ends a run waiting for an approval.

An agent turn started with wait: true, and cancelled while its model call ran, answers the waiting start with:

{
"error": {
"code": "agent-turn-aborted",
"message": "Agent turn cancelled",
"details": {
"reason": "external",
"runId": "8619fb26-6370-48df-b9bc-01bcd91602b6"
},
"requestId": "req-fb51fd31-fe44-4a44-ad97-88baa95a23e4"
}
}

Your app cancels a run with runs.cancel. Ask your coding agent: "I want my app to cancel a run it started." For reference, this code starts a run in the background, then cancels it:

app/cancel-reservation.ts
import { createClient } from '@kindgi/sdk/client';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN
const run = await kindgi.runs.start({
flow: 'acme.reserve-order',
input: { orderId: 'A-500' },
options: { wait: false },
});
const cancelled = await kindgi.runs.cancel(run.id);
console.log(cancelled.status);
cancelled

cancel returns the run, now cancelled. It answers with a conflict error when the run had already ended.

Cancelling a run withdraws the approvals it was waiting for, in the same step. So does it for the approvals of the runs inside it, such as a flow's agent steps. They leave the pending list as withdrawn, with withdrawnBecause: "run-cancelled". A reviewer who opens one can no longer decide it: it answers 409 approval-terminal.

An approval the run asked for without waiting on it stays pending, and can still be decided. A project-wide memory fact's review is one, and a memory erasure's is another.

To see them before you cancel, list the run's approvals with the runs inside it: approvals.list({ runId, includeDescendants: true }) in TypeScript, approvals.list(run_id=…, include_descendants=True) in Python.

Decide an approval shows how reviewers decide them.

A run's page has Cancel run while it's going, for project editors and up. Its dialog says what a cancel does before anything happens:

Cancel this run?
This stops the run and every step still going inside it. It ends as cancelled, and can't be continued.
  • A run waiting for an approval adds: "If it's waiting for an approval, that approval is withdrawn: it leaves the reviewers' list." See Approvals of a cancelled run.
  • Keep it running closes the dialog. Cancel the run cancels it, and the dialog says "Cancelled.".
  • A run that finished meanwhile: "It had already finished: there was nothing to stop."

The Cancel this run? dialog over a running flow run's page: "This stops the run and every step still going inside it. It ends as cancelled, and can't be continued.", with Keep it running and Cancel the run.

The run's page says Cancelled, and so does each step that was still going when the cancel landed: a loop's node, and the round that was running. Those steps have no end of their own in the journal, as What a cancel stops explains. So the Overview shows each with a plain circle and "Cancelled", not as still running. A step that finished as the cancel landed keeps its end.

Here a loop over six orders was cancelled while the turn for A-1002 ran:

A cancelled flow run's Overview: acme.each-order, Cancelled, its input of six orders, then under Steps "each Cancelled" and "answer Cancelled", each with a plain circle.

The Journal tab says so beside each of those starts. Its timeline for that run, without the times:

The run started
Started: step "each" Cancelled with the run: no end of its own
Done: step "answer" 1.7 s
Done: step "answer" 1.6 s
Started: step "answer" Cancelled with the run: no end of its own
The run was cancelled

A failed run shows its unfinished steps the same way: "Stopped: the run failed" on the Overview, and "Stopped when the run failed: no end of its own" in the Journal.