How it works
The pattern uses two services:CodingAgent— a Virtual Object, one per agent session. It holds the conversation history and the invocation ID of any running task.CodingTask— a long-running Service that performs the actual work (a lengthy LLM call, or a chain of them).
- The agent’s
messagehandler loads the conversation history from the Virtual Object state. - If a task is already running, it cancels that invocation and waits for the cleanup to finish.
- It sends a new task to the
CodingTaskservice and persists the new invocation ID. - Inside
CodingTask, the cancellation surfaces as a terminal error at the next Restate await. The task catches it, notifies the orchestrator for durable cleanup, and re-raises so Restate records the invocation ascancelled.
How does Restate help?
- Durable cancellation signals: Cancellation is a first-class, durable signal that propagates through nested invocations automatically.
- Cleanup always runs: Terminal errors surface at the next Restate await, giving the handler a chance to run durable cleanup steps before finishing.
- Concurrency control per session: Virtual Objects queue concurrent requests per key, so interruptions are processed one at a time without races.
- Observability: The Restate UI shows the cancelled invocation side-by-side with the one that replaced it.
Example
Restate TS
Restate Py
1. Interrupt on every new message
The orchestrator’smessage handler loads the conversation history, cancels any in-flight task, waits for its cleanup to finish, then dispatches a fresh task and stores its invocation ID for the next interruption. ctx.cancel sends a durable cancellation signal and ctx.attach blocks until the target has finished its cleanup; the cancelled invocation terminating with a TerminalError is the expected outcome:agent.ts
2. Catch the cancellation inside the task
On the other side, the cancellation surfaces as aCancelledError at the next ctx.run. A try/catch around the work lets you run durable cleanup before re-raising:agent.ts
Run this example
Run this example
Install Restate and launch it:Get the example:Start the agent service with your API key:Register the services with Restate:Happy path — one message, one full response:Interruption path — fire a first message, then interrupt before it finishes:Open the Restate UI at
http://localhost:9070 to inspect the invocations — the first CodingTask.runTask shows status cancelled, and the second completed.