require-abort-signal
This rule identifies streaming AI SDK calls (streamText, streamObject) that don't include an AbortSignal for cancella...
Ensures streaming calls have AbortSignal for graceful cancellation.
📊 Rule Details
| Property | Value |
|---|---|
| Type | suggestion |
| Severity | ⚪ LOW |
| OWASP LLM | LLM10: Unbounded Consumption |
| CWE | CWE-404: Improper Resource Shutdown |
| CVSS | 4.0 |
| Config Default | off (recommended), error (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
| Option | Type | Default | Description |
|---|---|---|---|
targetFunctions | string[] | ["streamText","streamObject"] | Functions that should have abort signal |
🛡️ 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
🔗 Related Rules
require-max-tokens- Limit token consumptionrequire-error-handling- Handle errors
🔄 Changes in the 2026-10-10 FP/FN audit
- A spread in the options may carry the signal and counts; SDK calls are matched by exact name.
- Now
offinrecommended: CVSS 4, and it fired on the SDK quickstart route. - Options: the documented
defaultOptionsnow apply. Earlier versions readcontext.optionsand silently ran a shorter hard-coded list instead.
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 internallyMitigation: Apply rule to wrapper implementations.
📚 References
Use this rule
npm install -D oxlint-plugin-vercel-ai-security{ "jsPlugins": ["oxlint-plugin-vercel-ai-security"], "rules": { "vercel-ai-security/require-abort-signal": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-vercel-ai-security'], rules: { 'vercel-ai-security/require-abort-signal': 'error', }, },});oxlint-plugin-vercel-ai-security re-exports eslint-plugin-vercel-ai-security/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.