AbortController API. This is useful for canceling long-running operations or implementing timeouts.
Basic Abort Example
import { query, isAbortError } from '@qwen-code/sdk';
const abortController = new AbortController();
const result = query({
prompt: 'Analyze the entire codebase',
options: {
abortController,
},
});
// Abort after 5 seconds
setTimeout(() => {
console.log('Aborting query...');
abortController.abort();
}, 5000);
try {
for await (const message of result) {
if (message.type === 'assistant') {
console.log(message.message.content);
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('Query was aborted');
} else {
throw error;
}
}
User-Initiated Abort
import { query, isAbortError } from '@qwen-code/sdk';
import * as readline from 'readline';
const abortController = new AbortController();
// Listen for Ctrl+C
process.on('SIGINT', () => {
console.log('\nReceived SIGINT, aborting query...');
abortController.abort();
});
const result = query({
prompt: 'Perform a long-running analysis',
options: {
abortController,
},
});
console.log('Query running. Press Ctrl+C to abort.\n');
try {
for await (const message of result) {
if (message.type === 'assistant') {
console.log(message.message.content);
} else if (message.type === 'result') {
console.log('\nQuery completed successfully');
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('\nQuery was aborted by user');
process.exit(0);
} else {
console.error('Error:', error);
process.exit(1);
}
}
Timeout with Abort
import { query, isAbortError } from '@qwen-code/sdk';
async function queryWithTimeout(
prompt: string,
timeoutMs: number
) {
const abortController = new AbortController();
const timeoutId = setTimeout(() => {
console.log(`Timeout of ${timeoutMs}ms exceeded, aborting...`);
abortController.abort();
}, timeoutMs);
try {
const result = query({
prompt,
options: {
abortController,
},
});
for await (const message of result) {
if (message.type === 'assistant') {
console.log(message.message.content);
} else if (message.type === 'result') {
clearTimeout(timeoutId);
return message.result;
}
}
} catch (error) {
clearTimeout(timeoutId);
if (isAbortError(error)) {
throw new Error(`Query timed out after ${timeoutMs}ms`);
}
throw error;
}
}
try {
const result = await queryWithTimeout(
'Analyze the project',
10000 // 10 second timeout
);
console.log('Result:', result);
} catch (error) {
console.error('Error:', error.message);
}
Conditional Abort Based on Progress
import { query, isSDKAssistantMessage, isAbortError } from '@qwen-code/sdk';
const abortController = new AbortController();
let messageCount = 0;
const MAX_MESSAGES = 10;
const result = query({
prompt: 'Analyze and refactor the codebase',
options: {
abortController,
permissionMode: 'auto-edit',
},
});
try {
for await (const message of result) {
if (isSDKAssistantMessage(message)) {
messageCount++;
console.log(`Message ${messageCount}:`, message.message.content);
// Abort if too many messages
if (messageCount >= MAX_MESSAGES) {
console.log('\nMax messages reached, aborting...');
abortController.abort();
}
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('Query aborted after', messageCount, 'messages');
} else {
throw error;
}
}
Multiple Queries with Shared Abort
import { query, isAbortError } from '@qwen-code/sdk';
const sharedAbortController = new AbortController();
async function runQuery(prompt: string, id: number) {
const result = query({
prompt,
options: {
abortController: sharedAbortController,
},
});
try {
for await (const message of result) {
if (message.type === 'result') {
console.log(`Query ${id} completed:`, message.result);
}
}
} catch (error) {
if (isAbortError(error)) {
console.log(`Query ${id} was aborted`);
} else {
throw error;
}
}
}
// Run multiple queries
Promise.all([
runQuery('Task 1', 1),
runQuery('Task 2', 2),
runQuery('Task 3', 3),
]);
// Abort all queries after 10 seconds
setTimeout(() => {
console.log('Aborting all queries...');
sharedAbortController.abort();
}, 10000);
Abort with Cleanup
import { query, isAbortError } from '@qwen-code/sdk';
import * as fs from 'fs/promises';
const TEMP_FILE = '/tmp/query-temp.txt';
async function runQueryWithCleanup() {
const abortController = new AbortController();
// Create temp file
await fs.writeFile(TEMP_FILE, 'Query in progress...', 'utf-8');
try {
const result = query({
prompt: 'Long running task',
options: {
abortController,
},
});
// Setup abort after 5 seconds
setTimeout(() => abortController.abort(), 5000);
for await (const message of result) {
if (message.type === 'result') {
await fs.writeFile(TEMP_FILE, 'Query completed', 'utf-8');
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('Query aborted, cleaning up...');
await fs.unlink(TEMP_FILE);
} else {
throw error;
}
}
}
runQueryWithCleanup().catch(console.error);
Abort with Query Instance Methods
import { query } from '@qwen-code/sdk';
const abortController = new AbortController();
const q = query({
prompt: 'Analyze code',
options: {
abortController,
},
});
console.log('Session ID:', q.getSessionId());
// Abort using the query instance
setTimeout(async () => {
if (!q.isClosed()) {
console.log('Interrupting query...');
await q.interrupt(); // Graceful interrupt
// Or force abort
// abortController.abort();
}
}, 5000);
for await (const message of q) {
console.log(message);
}
console.log('Query closed:', q.isClosed());
Abort with Multi-Turn Conversation
import { query, type SDKUserMessage, isAbortError } from '@qwen-code/sdk';
const abortController = new AbortController();
async function* conversation(): AsyncIterable<SDKUserMessage> {
const sessionId = 'abortable-session';
yield {
type: 'user',
session_id: sessionId,
message: { role: 'user', content: 'First task' },
parent_tool_use_id: null,
};
await new Promise(resolve => setTimeout(resolve, 2000));
// Check if aborted
if (abortController.signal.aborted) {
console.log('Conversation aborted before second message');
return;
}
yield {
type: 'user',
session_id: sessionId,
message: { role: 'user', content: 'Second task' },
parent_tool_use_id: null,
};
}
// Abort after 3 seconds (before second message)
setTimeout(() => {
console.log('Aborting conversation...');
abortController.abort();
}, 3000);
const result = query({
prompt: conversation(),
options: {
abortController,
permissionMode: 'yolo',
},
});
try {
for await (const message of result) {
if (message.type === 'assistant') {
console.log('Assistant:', message.message.content);
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('Multi-turn conversation was aborted');
} else {
throw error;
}
}
Graceful Shutdown
import { query, isAbortError } from '@qwen-code/sdk';
const abortController = new AbortController();
let shuttingDown = false;
async function gracefulShutdown() {
if (shuttingDown) return;
shuttingDown = true;
console.log('\nInitiating graceful shutdown...');
console.log('Waiting for current operation to finish...');
// Give 5 seconds for graceful shutdown
setTimeout(() => {
console.log('Force aborting...');
abortController.abort();
}, 5000);
}
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
const result = query({
prompt: 'Long running analysis',
options: {
abortController,
},
});
try {
for await (const message of result) {
if (shuttingDown) {
console.log('Shutdown requested, finishing current message...');
}
if (message.type === 'result') {
console.log('Analysis complete:', message.result);
}
}
} catch (error) {
if (isAbortError(error)) {
console.log('Query was aborted during shutdown');
process.exit(0);
} else {
console.error('Error:', error);
process.exit(1);
}
}
Error Type Checking
import { query, isAbortError, AbortError } from '@qwen-code/sdk';
const abortController = new AbortController();
setTimeout(() => abortController.abort(), 1000);
const result = query({
prompt: 'Quick task',
options: { abortController },
});
try {
for await (const message of result) {
console.log(message);
}
} catch (error) {
// Type guard method
if (isAbortError(error)) {
console.log('Aborted (type guard):', error.message);
}
// instanceof method
if (error instanceof AbortError) {
console.log('Aborted (instanceof):', error.name);
}
// Manual check
if (error && typeof error === 'object' && 'name' in error && error.name === 'AbortError') {
console.log('Aborted (manual check)');
}
}
See Also
- query() Function - AbortController option
- Query Instance Methods - interrupt() method
- Multi-Turn Example - Aborting conversations
