Cancellation
VoltAgent implements cancellation through the standard AbortController API. Pass an AbortController instance to agent methods to stop LLM generation, tool execution, and multi-agent workflows.
Basic Cancellation
Create an AbortController and pass it to agent methods:
import { Agent, isAbortError } from "@voltagent/core";
import { openai } from "@ai-sdk/openai";
const agent = new Agent({
name: "Assistant",
instructions: "A helpful assistant",
model: openai("gpt-4o"),
});
const abortController = new AbortController();
// Cancel after 5 seconds
setTimeout(() => {
abortController.abort("Timeout: Operation took too long");
}, 5000);
try {
const response = await agent.generateText("Write a detailed analysis...", {
abortController,
});
console.log(response.text);
} catch (error) {
if (isAbortError(error)) {
console.log("Operation cancelled:", error.message);
} else {
throw error;
}
}
How Cancellation Works
When you provide an AbortController to an agent method:
- The signal is passed to the LLM provider
- Tools receive the
OperationContextcontaining theabortController - Subagents inherit the parent's
abortController - All operations check the signal state before proceeding
Cancellation propagates through the entire operation chain for clean shutdown at every level.
The signal parameter is deprecated. Use abortController instead. The signal parameter will be removed in a future version.
Streaming Cancellation
Cancellation works with streaming responses:
const abortController = new AbortController();
// User can cancel anytime
document.getElementById("stop-btn")?.addEventListener("click", () => {
abortController.abort("User requested stop");
});
const response = await agent.streamText("Generate a long report...", {
abortController,
});
try {
for await (const chunk of response.textStream) {
process.stdout.write(chunk);
}
} catch (error) {
if (isAbortError(error)) {
console.log("\nStream cancelled");
}
}
With fullStream, you receive detailed cancellation feedback:
const response = await agent.streamText("Complex task...", {
abortController,
});
for await (const event of response.fullStream) {
if (event.type === "error" && isAbortError(event.error)) {
console.log("Cancelled during:", event.context);
break;
}
// Process other events
}
Tool Cancellation
Tools receive the OperationContext as the second parameter. Access abortController through the context to respond to or trigger cancellation:
import { createTool } from "@voltagent/core";
import { z } from "zod";
const dataProcessingTool = createTool({
name: "process_data",
description: "Process large datasets",
parameters: z.object({
dataset: z.string(),
operation: z.string(),
}),
execute: async (args, context) => {
const abortController = context?.abortController;
const signal = abortController?.signal;
// Check if already aborted
if (signal?.aborted) {
return { error: "Operation was already cancelled" };
}
// Tool can trigger abort based on conditions
if (args.dataset === "restricted") {
abortController?.abort("Access to restricted dataset denied");
return { error: "Dataset access denied" };
}
// Pass signal to async operations
try {
const response = await fetch(`/api/process/${args.dataset}`, {
method: "POST",
body: JSON.stringify({ operation: args.operation }),
signal,
});
return await response.json();
} catch (error) {
if (error.name === "AbortError") {
return { error: "Processing cancelled" };
}
throw error;
}
},
});
Multi-Agent Cancellation
In supervisor-subagent architectures, the abort signal propagates to all subagents:
const researcher = new Agent({
name: "Researcher",
instructions: "Research topics thoroughly",
model: openai("gpt-4o-mini"),
});
const writer = new Agent({
name: "Writer",
instructions: "Write detailed content",
model: openai("gpt-4o-mini"),
});
const supervisor = new Agent({
name: "Supervisor",
instructions: "Coordinate research and writing",
model: openai("gpt-4o"),
subAgents: [researcher, writer],
});
const abortController = new AbortController();
// Cancel supervisor and any active subagent operations
setTimeout(() => {
abortController.abort("Deadline reached");
}, 10000);
const response = await supervisor.streamText("Research and write about quantum computing", {
abortController,
});
When the supervisor is cancelled:
- Any active
delegate_taskoperations stop - Subagents inherit the parent's
abortController - All tool executions within subagents are cancelled
- The entire workflow stops
Timeout Implementation
Implement timeouts for operations:
const abortController = new AbortController();
// Set a timeout to abort after 30 seconds
const timeoutId = setTimeout(() => {
abortController.abort("Operation timeout - exceeded 30 seconds");
}, 30000);
try {
const response = await agent.generateText("Complex analysis...", {
abortController,
});
// Clear timeout if operation completes
clearTimeout(timeoutId);
console.log(response.text);
} catch (error) {
clearTimeout(timeoutId); // Always clear the timeout
if (isAbortError(error)) {
console.log("Operation timed out:", error.message);
} else {
throw error;
}
}