Skip to main content
The SDK supports aborting queries using the standard 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