AI Engineering
9 min read

AI Agent Cancellation: Stop the Work, Not Just the Stream

A practical guide to propagating deadlines through models, retrieval, tools and runtimes—and proving cancelled work stops before effects continue.

An open aged-brass clutch disconnects an oxblood leather drive belt from a halted cream-paper feed on a dark walnut workbench.
AI Engineering / 9 min read
AIENGINE

9 min read

Share

Pressing stop on an AI interface may close a stream while the expensive or consequential work continues. Retrieval queries can still scan, model calls can still generate, background tasks can still run and a tool can still send the email the user thought they cancelled. The interface has ended its interest in the result; the system has not necessarily ended the operation.

The engineering decision is therefore not “should this workflow have a stop button?” It is: for every child operation, must it stop, may it finish but be discarded, or has it crossed into a state that requires compensation? Answer that before connecting a model to real tools.

Cancellation is a protocol, not a UI event

Cancellation crosses several boundaries: browser to gateway, gateway to orchestrator, orchestrator to model and retrieval clients, tool broker to worker, and worker to the underlying database or process. A break at any boundary creates an orphan that can consume capacity or produce a late side effect.

The gRPC cancellation guide is explicit about the cooperative nature of the mechanism. A client can signal that it no longer wants an RPC result, but the library generally cannot interrupt an application handler. The handler must notice cancellation, stop its local processing and propagate the signal to work it started. Closing the downstream stream is only the first hop.

Cancellation also loses races. PostgreSQL's protocol documentation for cancelling a query says a cancel request may arrive after the query has finished and that the frontend receives no direct confirmation that cancellation succeeded. The correct state after “cancel sent” is not automatically “nothing happened”. It is “outcome still to be established”.

This matters more for AI agents because the visible generation is often the least consequential branch. A single run may have parallel search, reranking, code execution and tool calls behind it. Treat the run as a tree of owned work, not one HTTP response.

Separate five controls that are often called stop

Teams frequently collapse different mechanisms into one timeout setting. They solve different problems:

  • A deadline is the latest time at which the caller still values the result.
  • Cancellation asks work that is no longer needed to stop cooperatively.
  • Rejection prevents new work from starting when too little time or capacity remains.
  • Termination escalates from graceful shutdown to force when cooperation fails.
  • Compensation addresses an effect that has already committed and cannot be cancelled.

The gRPC deadline guidance recommends explicit realistic deadlines and propagating them to downstream RPCs. It also notes that the application remains responsible for stopping activities it spawned. The Go database cancellation example demonstrates the same lifetime model across application and database calls: derive the child context from the request context so a client disconnect or parent timeout can reach the query.

None of those mechanisms reverses an effect that already committed. Cancellation may stop a draft, query or process; it does not unsend a message or unsubmit an order. That boundary belongs to the compensating-actions field guide. Keep cancellation and compensation connected, but do not label one as the other.

Give every operation a cancellation class

Do not infer cancellation behaviour from whether a function is asynchronous. Record a contract beside each model, retriever and tool operation.

Operation classOn cancellationEvidence required before terminal state
Interruptible read or computePropagate signal and stop promptlyWorker acknowledged stop; no result admitted
Finite work safe to abandonLet it finish under a short cap; discard resultResult marked late and excluded from state, cache and response
External job with a cancel APISend cancel using stable job identity; poll or receive terminal statusExternal system reports cancelled, completed or failed
Transactional write before commitAbort and roll backTransaction outcome is known
Committed or irreversible effectDo not pretend to cancel; reconcile or compensateEffect identifier, committed outcome and recovery owner

This table is an AIEngine operating synthesis, not a claim that one source prescribes these exact five classes. Its purpose is to force a decision where vendor APIs leave ambiguity.

For each contract, also record the cancellation check frequency, grace period, force-stop mechanism, cleanup behaviour, retry policy and late-result rule. If the dependency has no cancellation capability, say so. “The SDK promise rejected” is not evidence that the remote work stopped.

Long-running durable work needs a liveness channel. Temporal's activity cancellation documentation states that activities must heartbeat to receive cancellation from the service; an activity may also accept or ignore the request. That is a useful general design test: if a detached worker neither polls an owned cancellation record nor heartbeats to an orchestrator, how will it learn that its parent ended?

Carry one shrinking lifetime through the graph

Set one absolute top-level deadline from the product decision, not a fresh full timeout at every hop. Each child receives the earlier of the parent deadline and its own local safety cap. Reserve time at the end for assembling a response, recording outcomes and performing bounded cleanup.

An eight-second interactive run might allocate at most two seconds to retrieval, four to generation and leave two for validation, response and cleanup. If retrieval consumes 1.7 seconds, generation does not receive a new four-second entitlement that pushes the run past its original deadline. It receives the remaining budget minus the protected reserve.

Reject a child before dispatch when its minimum useful execution time no longer fits. The broader backpressure guide explains why accepting work that will expire creates retry load and stale queues. Cancellation handles work already admitted; it should not replace admission control.

Use one request-scoped cancellation signal through adapters rather than storing a global signal on a shared agent object. Web runtimes provide a practical model: an AbortSignal can carry an explicit abort or timeout and can combine signals, but an aborted signal is one-use. A new run needs a new scope. A background job that intentionally outlives the request needs an explicit new owner, identity, deadline and authority—not an accidental escape into an uncancellable global queue.

Make cancellation an observable handshake

A Boolean such as cancelled=true hides the race between request, observation and completion. Use an append-only lifecycle:

  • requested: a user, deadline, policy or parent asks the operation to stop;
  • observed: the responsible worker receives that request;
  • accepted: the worker stops dispatching children and begins bounded cleanup;
  • terminal: the operation is confirmed cancelled, completed, failed, force-stopped or unknown.

Attach reason, request time, observer, operation identity, parent identity and last known commit boundary. Let late completions transition to completed_after_cancel rather than overwrite history with ordinary success. Downstream state reducers must reject results whose parent is terminal or whose authority version no longer matches.

Retries need the same discipline. AWS's reliability guidance on limiting retries warns against retrying at multiple layers, retrying non-idempotent work and allowing unbounded elapsed time. Cancellation, deadline expiry and explicit user stop are normally terminal reasons for that run, not transient failures to retry under a new clock.

Preserve the chain in traces and durable records. The incident-replay guide shows how to bind events to model, prompt, tool and policy versions. For cancellation, add the parent run, child operation, remaining deadline at dispatch, cancel reason, time to observation, final outcome and any effect identifier.

Work a policy-assistant cancellation

Consider a supplier-policy assistant with an eight-second response deadline. It retrieves approved documents, asks a model for a cited draft and may create an internal case note only after validation. This is a design example, not a report of an AIEngine deployment.

At 2.4 seconds, the user closes the request while retrieval and generation are in flight:

  • The gateway records cancel_requested once and cancels the request scope.
  • The orchestrator stops accepting model tokens and passes the same cancellation to active retrieval, model and tool clients.
  • The tool broker refuses any not-yet-dispatched write because the parent is no longer active.
  • Retrieval attempts to cancel the database query, but the run remains non-terminal until the query responds or its own deadline expires.
  • Any result arriving after the parent ended is stored only as execution evidence; it cannot enter conversation memory, a shared answer cache or a later tool plan.
  • If the case-note transaction committed before cancellation was observed, the operation becomes completed_after_cancel; its note identifier and outcome are surfaced for policy-driven retention or deletion.

The design keeps side effects behind the final gate, which narrows the race. It does not claim that ordering eliminates it. Use idempotency and fencing for duplicates and stale workers as described in the agent-concurrency guide.

Escalate when cooperative stop fails

Cooperative cancellation should have a bounded grace period. After it expires, isolate or terminate the worker according to consequence. Kubernetes documents a comparable two-stage process for Pod termination: the runtime normally sends a termination signal with a grace period, then force-kills remaining processes. Its documentation also cautions that immediate deletion does not wait for confirmation that the running resource has stopped.

That last warning is essential for agents. Removing a job row, hiding a task in the UI or deleting an orchestration object is not proof that its process, browser session or remote job ended. Keep the execution identity discoverable until terminal evidence arrives.

Shield only the cleanup that must survive cancellation: releasing a lease, closing a sandbox, recording an outcome or revoking short-lived authority. Give that cleanup its own stricter deadline and no permission to start new business work. If force-stop can leave partial files, locked records or an unknown remote outcome, route those artefacts to a named reconciler.

Test races and measure stopped work

Happy-path cancellation tests prove almost nothing. Inject cancellation before child dispatch, during retrieval, between model completion and validation, immediately before and after a transaction commit, during retry backoff, while a worker is disconnected and while cleanup hangs.

The release gate should demonstrate:

  • no new child starts after the parent cancellation is observed;
  • every child reaches a known terminal or explicit unknown state;
  • late results cannot enter response, memory, cache or a future plan;
  • retries do not restart a user-cancelled or expired run;
  • force-stop occurs only after the declared grace period; and
  • committed effects retain an identifier and recovery route.

Track cancellation requests by reason, p50 and p95 request-to-observation time, p95 observation-to-terminal time, compute consumed after deadline, orphan children, results rejected as late, side effects completed after cancellation, unknown outcomes and force-stop rate. Review by model, retriever, tool and adapter; an excellent overall median can hide one connector that never stops.

The practical target is not “zero cancelled requests”. Users change their minds, deadlines expire and redundant branches lose races. The target is a system that makes stopping normal, bounded and provable. A stop button becomes trustworthy only when every owned operation either stops, is safely discarded, or leaves an attributable effect with a recovery path.

TaggedAI Agent CancellationDeadline PropagationAI ReliabilityTool CallingDistributed SystemsAI Operations
Work With Us

Interested in implementing this for your business?

We help UK businesses put these ideas into practice. Book a call to discuss your specific situation.