A fetch call with no timeout is a connection that can remain open indefinitely if the upstream server is slow, overloaded, or adversarially slow. On serverless platforms, an open connection holds the function invocation alive until the platform's hard execution limit — 10 seconds on Vercel Hobby, 60 seconds on Pro — burning compute the entire time. CWE-400 (Uncontrolled Resource Consumption) applies: the user who triggered the request pays for exactly one slow upstream response, but a platform attack that sends many such requests exhausts the concurrency pool and causes 503s for all users. Explicit timeouts also force proper error handling — without them, timeout errors surface as generic network failures with no useful signal.
Low because a missing timeout primarily affects availability under adversarial upstream conditions, rather than enabling immediate financial or data-loss impact.
Pass AbortSignal.timeout(ms) as the signal option to every fetch call. Set timeouts appropriate to the SLA of the endpoint being called — 5 seconds is generous for most REST APIs.
// Bad: hangs until platform limit
const res = await fetch(url)
// Good: 5-second abort
const res = await fetch(url, {
signal: AbortSignal.timeout(5000),
})
if (!res.ok) throw new Error(`Upstream ${res.status}: ${url}`)
For Axios in src/lib/http.ts, set a module-level default: axios.defaults.timeout = 5000. For SDK clients (OpenAI, Stripe), check the SDK's timeout constructor option and set it at singleton construction time.
ID: ai-slop-cost-bombs.unrate-limited-spend.external-api-call-has-timeout
Severity: low
What to look for: Walk source files for fetch(, axios., ky., got( call sites. For each call site, count all calls and verify each call passes a timeout option: fetch(url, { signal: AbortSignal.timeout(...) }), axios.get(url, { timeout: ... }), ky.get(url, { timeout: ... }), got(url, { timeout: ... }), OR uses a globally-configured client with a default timeout.
Pass criteria: 100% of external API calls have an explicit timeout. Report: "X external API calls, Y with timeout, 0 unbounded."
Fail criteria: At least 1 external API call has no timeout option.
Skip (N/A) when: No external API call patterns found in source.
Detail on fail: "3 fetch calls without timeout: src/app/api/proxy/route.ts uses fetch(externalUrl) — if the upstream hangs, the request hangs forever and consumes a connection"
Remediation: A fetch with no timeout is a connection that can hang for hours, exhausting your connection pool. Always set a timeout:
// Bad: hangs forever if upstream is slow
const res = await fetch(url)
// Good: 5-second timeout
const res = await fetch(url, { signal: AbortSignal.timeout(5000) })