Interlace ESLint
ESLint Interlace
Vercel AIRules

require-abort-signal

This rule identifies streaming AI SDK calls (`streamText`, `streamObject`) that don't include an AbortSignal for cancellation.

Ensures streaming calls have AbortSignal for graceful cancellation.

📊 Rule Details

PropertyValue
Typesuggestion
Severity⚪ LOW
OWASP LLMLLM10: Unbounded Consumption
CWECWE-404: Improper Resource Shutdown
CVSS4.0
Config Defaultoff (recommended), warn (strict)

🔍 What This Rule Detects

This rule identifies streaming AI SDK calls (streamText, streamObject) that don't include an AbortSignal for cancellation.

❌ Incorrect Code

// No abort signal
await streamText({
  model: openai('gpt-4'),
  prompt: 'Stream a long response',
});

// Missing signal in streamObject
await streamObject({
  model: anthropic('claude-3'),
  prompt: 'Generate object',
  schema: mySchema,
});

✅ Correct Code

// With abort signal
const controller = new AbortController();
await streamText({
  model: openai('gpt-4'),
  prompt: 'Stream a long response',
  abortSignal: controller.signal,
});

// Using signal property
await streamObject({
  model: anthropic('claude-3'),
  prompt: 'Generate object',
  schema: mySchema,
  signal: abortController.signal,
});

⚙️ Options

OptionTypeDefaultDescription
signalPropertyNamesstring[]['abortSignal', 'signal']Property names that provide abort signals

🛡️ Why This Matters

Without abort signals:

  • Resource leaks - Streams continue after user navigation
  • Wasted costs - Tokens consumed after request cancelled
  • Server overhead - Backend continues processing
  • Poor UX - No way to cancel long operations

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Signal from Variable

Why: Signal stored in variables is not analyzed.

// ❌ NOT DETECTED - Signal from variable
const options = { signal: controller.signal };
await streamText({ model: openai('gpt-4'), ...options });

Mitigation: Use inline signal property.

Framework-Provided Signal

Why: Framework signals are not visible.

// ❌ NOT DETECTED (correctly) - Next.js handles signals
export async function GET(req: Request) {
  // req.signal provided by Next.js
  return streamText({ ..., abortSignal: req.signal });
}

Mitigation: Document framework signal handling.

Wrapper Functions

Why: Custom wrappers may include signal internally.

// ❌ NOT DETECTED - Wrapper adds signal
await myStreamText(prompt); // Wrapper adds signal internally

Mitigation: Apply rule to wrapper implementations.

📚 References

On this page