cd /news/ai-agents/your-ai-agent-needs-a-cancellation-c… · home topics ai-agents article
[ARTICLE · art-103449] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Your AI Agent Needs a Cancellation Contract, Not Just a Stop Button

A developer argues that AI agents need a durable cancellation contract rather than a simple stop button, proposing a state machine with cancel versions and compare-and-set operations to prevent resurrected work and reconcile side effects. The approach separates cancellation from process liveness and requires checks at every work-creating boundary.

read4 min views3 publishedAug 19, 2026

A stop button is not a cancellation protocol.

In a toy agent, “stop” can mean setting a boolean and waiting for the loop to exit. In a real agent, work may already be queued, claimed by another worker, inside a browser session, or waiting for an outbound side effect. If cancellation is not represented as durable state, a restart can resurrect work the operator thought they stopped.

The useful question is not “did the process receive SIGTERM?” It is:

Can every layer prove whether this run may still start work, whether in-flight work must finish, and what happened to side effects that were interrupted?

This article turns cancellation into a small contract you can test.

Keep cancellation separate from process liveness. A worker can be alive while its run is cancelled, and a worker can die before it records the cancellation.

A minimal run state machine is:

Store the state durably with a monotonically increasing cancel_version:

run_id              status       cancel_version  updated_at
run_42              CANCELLING   3               2026-08-19T12:00:00Z

Workers must carry the version they observed. A dispatch is valid only if the durable row still says ACTIVE with the same version. This closes the race where an operator clicks Stop after a worker checked the run but before it starts a tool call.

A cancellation check only at the top of the agent loop is too weak. Check the contract at each boundary that can create work:

That last distinction matters. Cancelling a code-generation run does not automatically prove that an already-created notification was unsent. Execution and delivery need separate records.

Cooperative cancellation is the default: the worker notices the state change at safe checkpoints and exits cleanly. Forced cancellation is a deadline for the worker that does not cooperate.

A practical sequence is:

ACTIVE
  -> CANCELLING (revoke admission and retries)
  -> drain safe checkpoints
  -> CANCELLED (if no in-flight effects remain)
  -> UNKNOWN (if an external effect cannot be reconciled)

Do not mark a run CANCELLED merely because the worker process exited. A process can die after sending a request and before recording the response. For every external side effect, record an intent with a stable key before dispatch, then reconcile UNKNOWN using the provider’s lookup API, webhook, or an operator decision.

A cancellation timeout should transition the run to UNKNOWN or CANCELLING_TIMEOUT, not silently to success or cancellation. That makes the ambiguity visible instead of converting it into duplicate work on restart.

The critical operation is a compare-and-set, not a read followed by a write:

UPDATE runs
SET status = 'CANCELLING',
    cancel_version = cancel_version + 1,
    updated_at = CURRENT_TIMESTAMP
WHERE run_id = :run_id
  AND status = 'ACTIVE';

A worker dispatch can then require the exact version it observed:

UPDATE steps
SET status = 'DISPATCHED', dispatch_version = :cancel_version
WHERE step_id = :step_id
  AND status = 'CLAIMED'
  AND EXISTS (
    SELECT 1 FROM runs
    WHERE run_id = :run_id
      AND status = 'ACTIVE'
      AND cancel_version = :cancel_version
  );

If the update affects zero rows, the worker must not call the tool. It should release the claim and record CANCELLED_BEFORE_DISPATCH.

A cancellation feature is incomplete until it survives these injected failures:

Failure Expected evidence
Cancel between queue claim and dispatch No tool request, or a reconciled effect record
Worker s after the state check Stale version is rejected at dispatch
Process dies after provider request Effect becomes UNKNOWN, then reconciles
Retry timer fires after cancellation Retry is rejected and recorded
Cancellation store is unavailable Fail closed for new effects; preserve the run as unresolved
Browser action is mid-flight No next mutation; current action is explicitly unresolved
Controller restarts during drain Durable CANCELLING state resumes the drain

For each case, assert both safety and evidence: no unauthorized new effect, no lost cancellation intent, and a record an operator can explain later.

If you run an always-on OpenClaw or browser agent, a managed runtime such as managed OpenClaw hosting on Ampere can be one deployment option to evaluate. It does not replace durable run state, fencing, credential scope, or reconciliation. Those remain properties of the agent control plane.

Before trusting a Stop button, verify that:

The practical goal is not instant termination. It is a system that can prove what was prevented, what was already in flight, and what still needs reconciliation. That is the difference between a UI button and an operational cancellation contract.

If you are building coding agents or automation that must survive restarts and operator intervention, follow for more concrete control-plane tests and failure drills.

── more in #ai-agents 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/your-ai-agent-needs-…] indexed:0 read:4min 2026-08-19 ·