Vercel Function Timeout — Fix 504 and FUNCTION_INVOCATION_TIMEOUT
Your Vercel Serverless Function hit the plan timeout limit or an upstream API hung. Here is how to diagnose the exact cause, raise the maxDuration, use streaming to buy more time, and push long work to a background queue.
Timeout limits by plan
| Plan | Serverless Functions | Edge Functions |
|---|---|---|
| Hobby | 10 s max | 25 s max |
| Pro | 60 s max | 25 s max |
| Enterprise | 300 s max | 25 s max |
Edge Function limits are fixed at 25 s on every plan — maxDuration has no effect on Edge runtime.
Step-by-step fixes
1. Identify whether it is Vercel or an upstream API
Open your deployment in the Vercel dashboard and go to the Functions tab. A Vercel-side timeout shows the message FUNCTION_INVOCATION_TIMEOUT with a duration equal to your plan limit. If the log shows a network error from inside your code (e.g. FetchError: network timeout or an Axios ECONNABORTED) the upstream service hung — Vercel itself was still running.
AbortController signal on every external fetch call so a slow third-party service does not burn your entire Vercel budget:
// Always add an explicit timeout to external fetch calls
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), 8000); // 8 s
try {
const res = await fetch('https://slow-api.example.com/data', {
signal: controller.signal,
});
const data = await res.json();
return Response.json(data);
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') {
return new Response('Upstream API timed out', { status: 504 });
}
throw err;
} finally {
clearTimeout(id);
} 2. Increase maxDuration for Serverless Functions
Set maxDuration in vercel.json under the functions key, or export the constant directly from the route file. The value is in seconds and is silently clamped to your plan maximum.
// vercel.json — apply to a specific function path
{
"functions": {
"api/heavy-task.ts": {
"maxDuration": 60
}
}
} // Next.js App Router — export from the route file instead
// app/api/heavy-task/route.ts
export const maxDuration = 60; // seconds (Pro plan)
export async function POST(req: Request) {
// your long-running logic here
} - Edge runtime ignores maxDuration: if your route has
export const runtime = 'edge', remove it and let the function run as a Serverless Function to benefit frommaxDuration. - Verify in deployment output: after deploying, the Functions tab shows the configured
maxDurationnext to each function — confirm the value was accepted.
3. Use streaming to avoid visible timeouts
Vercel measures function execution time from the first invocation to the last byte sent. If you start streaming immediately, the connection stays open without triggering an early timeout — and the client receives data progressively instead of waiting for the full response. This is the standard pattern for AI completions and large data exports.
// app/api/stream/route.ts
export const maxDuration = 60;
export async function GET() {
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
for (let i = 0; i < 10; i++) {
// simulate chunked work
await new Promise(r => setTimeout(r, 4000));
controller.enqueue(encoder.encode(`chunk ${i}\n`));
}
controller.close();
},
});
return new Response(stream, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
} 4. Offload work to a background queue (QStash or Vercel Cron)
Work that exceeds 60 seconds even on Pro — report generation, batch AI processing, large file transforms — must be decoupled from the HTTP request entirely. The pattern: the user-facing endpoint returns 202 Accepted immediately and enqueues a job; a separate worker endpoint does the heavy work on its own schedule.
// app/api/enqueue/route.ts — returns immediately
import { Client } from '@upstash/qstash';
const qstash = new Client({ token: process.env.QSTASH_TOKEN! });
export async function POST(req: Request) {
const body = await req.json();
await qstash.publishJSON({
url: `${process.env.VERCEL_URL}/api/worker`,
body,
retries: 3,
});
return Response.json({ status: 'queued' }, { status: 202 });
} // app/api/worker/route.ts — does the heavy work
// vercel.json sets maxDuration: 300 for this path (Enterprise)
export const maxDuration = 300;
export async function POST(req: Request) {
const payload = await req.json();
// long-running processing here ...
return Response.json({ done: true });
} For scheduled batch work (nightly exports, hourly syncs) use Vercel Cron Jobs configured in vercel.json — they invoke a function on a cron schedule rather than on user request, so you are not racing against an HTTP timeout.
// vercel.json — cron job example
{
"crons": [
{
"path": "/api/nightly-sync",
"schedule": "0 2 * * *"
}
]
} Get an email the next time Vercel goes down
Outage alerts for Vercel, straight to your inbox. No account needed, unsubscribe in every email.
Watching more than one service? A free account covers 5 services + a daily digest — and Pro is currently free.
FAQ
What is the default Vercel Function timeout?
Serverless Functions time out at 10 seconds on Hobby, 60 seconds on Pro, and 300 seconds on Enterprise. Edge Functions are capped at 25 seconds on all plans regardless of maxDuration. Exceeding the limit produces a FUNCTION_INVOCATION_TIMEOUT error and a 504 HTTP response.
How do I increase the Vercel function timeout?
Set maxDuration in vercel.json under the functions key, or export maxDuration from your Next.js route file. Values are clamped to your plan limit. This has no effect on Edge Functions.
What is the difference between 504 and FUNCTION_INVOCATION_TIMEOUT?
FUNCTION_INVOCATION_TIMEOUT is the internal Vercel error meaning the function ran past its plan limit. The HTTP response sent to the caller is 504 Gateway Timeout. A 504 can also come from an upstream API hanging inside your function — check the Functions log to see which one.
How do I handle long-running tasks on Vercel Hobby plan?
Use streaming (start sending bytes immediately), or decouple via QStash / Vercel Cron so the HTTP response returns in under 10 s and the heavy work runs separately. Upgrading to Pro raises the limit to 60 s which covers most LLM API calls.