How to debug Vercel function timeouts
Explore with AI
A Vercel function timeout is a 504 FUNCTION_INVOCATION_TIMEOUT: the invocation ran past its maximum duration (300 seconds by default with Fluid compute) and Vercel stopped it. Find the failing route with vercel logs, time it with vercel httpstat, and search the logs for timeout and ECONNREFUSED to see whether the cause is an upstream call, a code path with no response, a loop or long work. Fix that cause first, raise maxDuration only for work that needs the time, and check the fix on a preview deployment before you ship it.
On this page
- What FUNCTION_INVOCATION_TIMEOUT means on Vercel
- The four things that run a Vercel function out of time
- Finding the cause of your timeout, step by step
- Fixing each cause
- Proving the fix on a preview deployment
- Where Vercel timeout fixes go wrong
- Keeping Vercel timeouts from coming back
- Investigating Vercel timeouts with Polylane
- Common questions
A Vercel function timeout means one invocation ran longer than its maximum duration, so Vercel stopped it. The caller gets a 504 with the code FUNCTION_INVOCATION_TIMEOUT. With Fluid compute, Node.js, Bun and Python functions get 300 seconds by default on every plan. A function that hits that limit has usually been stuck waiting: on a database or API that never answered, on a code path that never sent a response, or on a loop that never ended.
So debugging a timeout means finding where the time went. Confirm the timeout in the runtime logs, measure the route, check its configuration and time each await inside the handler. Then fix the cause, and raise maxDuration only when the work needs that long. Every command below is a Vercel CLI command that works on a linked project. Run vercel link first if you haven’t.
What FUNCTION_INVOCATION_TIMEOUT means on Vercel
Max duration is the longest one invocation can run before Vercel stops it. For a request handler, the clock covers handling the request and sending the response, and that includes streamed responses. When time runs out, Vercel returns 504 FUNCTION_INVOCATION_TIMEOUT: Gateway Timeout.
Hobby
Pro
Enterprise
Hobby is capped at 300 seconds, and you can’t raise it. Pro and Enterprise can set a function to 800 seconds. A beta raises that to 1,800 seconds (30 minutes) for specific runtime versions. Edge runtime functions follow a different rule: they must begin sending a response within 25 seconds.
The limit protects you as well. Vercel sets a default maximum so a runaway function can’t use resources without end. A timeout is the symptom. The cause is whatever the function was doing when the clock ran out.
The four things that run a Vercel function out of time
Vercel’s error reference names four causes. Each one leaves a different trace:
- Slow work. An API or database request takes longer than the function’s limit. In the logs you see the request start, maybe some progress, then nothing until the 504.
- No response. Every code path must return an HTTP response, even if it’s an error. If a branch returns nothing, Vercel waits until the maximum duration has passed and then times out. Vercel calls out unhandled exceptions as a common cause. The log usually shows an error line, then silence until the timeout.
- An infinite loop. A loop or recursive call never ends. Vercel’s guidance: if the logs show a function running right up to the limit with no output, a loop that never finishes is the likely cause.
- Upstream errors. An external API or database that the function calls is failing or refusing connections. You will often find
timeoutorECONNREFUSEDin the logs.
Slow functions that stay under the limit come from the same causes, plus cold starts, too little memory and a region far from your data. Fixing those makes a timeout less likely the next time traffic spikes.
Finding the cause of your timeout, step by step
- Confirm the 504s and find the routes. The JSON output of
vercel logsincludes each request’srequestPathandresponseStatusCode, so you can filter for timeouts:
vercel logs --environment production --source serverless --since 1h --json | jq 'select(.responseStatusCode == 504) | .requestPath'
--source serverless limits results to Vercel Functions and leaves out static assets and CDN requests.
- Read the full log messages for those requests.
vercel logs --environment production --source serverless --since 1h --expand
-
Rank routes by duration. The JSON output doesn’t include duration. Open the Vercel Functions tab in Observability on the dashboard and sort by duration to see which routes run closest to the limit.
-
Measure one endpoint.
vercel httpstatbreaks a request into DNS lookup, TCP connection, TLS handshake, server processing and content transfer:
vercel httpstat /api/slow-endpoint
Server processing is the time spent inside your function. If that number is the biggest, the problem is in your code or in what it calls.
- Check what the function runs with.
vercel inspect DEPLOYMENT_URL
Check three things: memory (a function with too little gets CPU-throttled), region (a function far from its database adds latency to every query) and max duration (the limit you are actually hitting).
- Search for upstream failures.
vercel logs --environment production --query "timeout" --since 1h --expand
vercel logs --environment production --query "ECONNREFUSED" --since 1h --expand
Matches here point to a dependency more than to your own code. The External APIs tab in Observability shows latency for every external call your functions make. Sort it by P75 to find the slowest upstream service.
- Rule cold starts in or out. Call the endpoint three times in a row:
vercel httpstat /api/slow-endpoint
vercel httpstat /api/slow-endpoint
vercel httpstat /api/slow-endpoint
If only the first call is much slower, a new instance is starting up. That rarely accounts for a full 300 seconds by itself, but it can push a function that is already slow over the limit.
Runtime logs expire fast on Hobby
Runtime logs are kept for 1 hour on Hobby, 1 day on Pro and 3 days on Enterprise. If a timeout happened overnight on Hobby, the evidence may already be gone. Each log line can be up to 256 KB. You can also open the logs at the /_logs path on the deployment URL, for example https://my-deployment-my-username.vercel.app/_logs. On Pro and Enterprise, set up a Log Drain so the logs reach a tool that keeps them longer.
Timing every await inside the handler
The 504 tells you that the function ran out of time. It doesn’t tell you which call it was waiting on. I built and led the Workers observability team at Cloudflare, and the same habit applies on Vercel: log how long each outbound call takes, so the last timing line before the silence names the culprit. Vercel Functions support the full console API, including console.time and console.timeEnd:
export async function GET(request: Request) {
console.time('db:orders');
const orders = await db.query('select id, total from orders where user_id = $1', [userId]);
console.timeEnd('db:orders');
console.time('api:pricing');
const res = await fetch('https://pricing.example.com/v1/quote');
console.timeEnd('api:pricing');
return Response.json({ orders, prices: await res.json() });
}
Redeploy, trigger the route and read the runtime logs. A healthy request prints both lines:
db:orders: 41.207ms
api:pricing: 312.554ms
A request that times out prints db:orders and then nothing. That tells you the pricing call never returned.
Fixing each cause
An upstream call that hangs needs a deadline
Vercel’s advice for upstream failures is to handle the error and return a response, so you don’t wait forever. Give every outbound call a deadline well under the function’s limit, and turn a failure into a clear status code:
export async function GET() {
try {
const res = await fetch('https://pricing.example.com/v1/quote', {
signal: AbortSignal.timeout(8000),
});
if (!res.ok) {
return Response.json({ error: 'pricing upstream error' }, { status: 502 });
}
return Response.json(await res.json());
} catch (err) {
console.error('pricing call failed', err);
return Response.json({ error: 'pricing unavailable' }, { status: 503 });
}
}
The function now fails in eight seconds with a log line that names the dependency. Your client can retry a 503, and nothing hangs for five minutes. Do the same for database clients: set the driver’s query and connection timeouts, and use connection pooling so each request doesn’t open a new connection.
Every code path has to send a response
The missing-response bug usually lives in an early return or a catch block that logs and stops. This Pages Router handler times out on any request that fails validation:
export default async function handler(req, res) {
const body = parse(req.body);
if (!body) {
console.error('invalid body');
return; // no response sent: Vercel waits until max duration
}
res.status(200).json(await process(body));
}
The fix is to send a response on every branch, including errors:
export default async function handler(req, res) {
try {
const body = parse(req.body);
if (!body) {
res.status(400).json({ error: 'invalid body' });
return;
}
res.status(200).json(await process(body));
} catch (err) {
console.error('handler failed', err);
res.status(500).json({ error: 'internal error' });
}
}
A loop that never exits
Look for while loops whose exit condition depends on data that may never arrive, pagination that follows a cursor without checking whether the cursor changed, and retries with no maximum count. Give each loop an explicit cap. Log the iteration count so a runaway shows up in the logs before the limit does.
Long work that needs more time: raise maxDuration
If the work is long by nature (a report, a batch import, a long model call), give that route more time. On Next.js App Router, export it from the route file:
// app/api/report/route.ts
export const maxDuration = 800;
On the Pages Router and Node.js /api routes, use the config object:
export const config = {
maxDuration: 800,
};
For older Next.js versions, and for Rust, Go, Python or Ruby, set it per path in vercel.json:
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"functions": {
"api/report.js": { "maxDuration": 800 },
"api/*.js": { "maxDuration": 60 }
}
}
For Python framework apps (FastAPI, Flask, Django), the whole app builds into one function. Use the entrypoint file as the key, for example app/main.py.
These limits assume Fluid compute, which is on by default for new projects. On an older project, turn it on: open the project, go to Settings, select Functions, enable the Fluid Compute toggle, click Save and redeploy.
For durations above 800 seconds, the beta needs per-function configuration on nodejs20.x, nodejs22.x, nodejs24.x, Bun 1.x or 1.4.x, or python3.12 to python3.14.
Work the caller doesn’t need to wait for
If the slow part runs after you already know the answer (sending an email, writing analytics, warming a cache), send the response first and keep working in the background with waitUntil, or with after in Next.js. For AI responses, stream, so output starts before the full computation finishes. For work longer than any single request should take, Vercel points to Workflows, which can pause, resume and keep state without a duration limit.
Placement and resources
If server processing time is high but no single call stands out, look at where and how the function runs. Functions run in iad1 by default, so move the region next to your database if it lives elsewhere. Raise memory from the 2 GB default if the function does heavy computation, because more memory also gives it more CPU. Run independent calls in parallel with Promise.all. To shorten cold starts, cut unused dependencies from the bundle and move expensive setup outside the request handler.
Proving the fix on a preview deployment
- Deploy a preview:
vercel deploy. - Measure the same route against it and compare with your earlier numbers:
vercel httpstat /api/slow-endpoint --deployment PREVIEW_URL
- Check that it still returns the right response:
vercel curl /api/slow-endpoint --deployment PREVIEW_URL
- To test a deadline fix, point the preview at a dependency you know is slow, and confirm the route returns its 503 well before the limit.
- Ship with
vercel deploy --prod, then watch real traffic:
vercel logs --environment production --source serverless --since 5m
Where Vercel timeout fixes go wrong
- Raising maxDuration over a hang. A call that never returns will use 800 seconds just as it used 300. You also pay provisioned memory for the instance all that time. Fix: add the deadline first, and raise the limit only for work that finishes.
- Expecting Hobby to go past 300 seconds. Hobby’s default and maximum are both 300 seconds. Fix: move the work out of the request with
waitUntilor Workflows, or upgrade to Pro. - Setting a project default above 800 seconds. During the beta, values above 800 seconds only work per function, in code or in
vercel.json. Fix: set it on the route. - Expecting the beta on Secure Compute or Static IPs. Neither supports durations above 800 seconds during the beta.
- A vercel.json glob that never matches. Pattern order matters. In a Next.js project that uses the
srcdirectory, paths need the/src/prefix. Fix: check the limit withvercel inspectafter you deploy. - Streams that idle out on HTTP/1.1. For long-running handlers on HTTP/2, Vercel sends
PINGframes while the response sits idle. HTTP/1.1 has nothing like that, so clients or proxies may close the connection. Fix: stream progress or heartbeat data while the work runs. - Forgetting the Edge rule. Edge runtime functions must begin a response within 25 seconds, whatever
maxDurationsays. Fix: start streaming early, or move the route to Node.js. - Running out of file descriptors. Each instance has 1,024 file descriptors shared across concurrent executions. With Fluid compute’s concurrency, sockets that leak can starve other requests on the same instance. Fix: close connections and reuse a pooled client.
- Debugging a proxied rewrite as a function. A request proxied to an external origin has its own 120 second proxied request timeout in Vercel’s general limits. Fix: check whether the route is a rewrite before you change function settings.
Keeping Vercel timeouts from coming back
Put a deadline on every outbound call, and keep those console.time lines on the calls that matter. Check the Observability Functions tab sorted by duration after each release, and the External APIs tab sorted by P75, so you catch a dependency that is slowing down before it reaches the limit. On Pro and Enterprise, send logs through a Log Drain into the tool that pages you, and alert on 504s per route. Then a single slow endpoint shows up within minutes, and you are not relying on log retention.
Investigating Vercel timeouts with Polylane
Polylane connects to Vercel with OAuth and can query its logs and metrics, so a Vercel project counts as a telemetry source without a separate observability tool. Alerts that fire on a connected Vercel account become issues. A fix run reads the evidence, traces the cause and opens a pull request when a code change fixes it, and the merge stays with you.
Running on Vercel? See how Polylane monitors Vercel in production.
Common questions.
What is the maximum duration of a Vercel Function on the Hobby plan?
With Fluid compute, Hobby functions on Node.js, Bun and Python have a 300 second default and a 300 second maximum, and you can't raise it. Pro and Enterprise also default to 300 seconds but can go to 800 seconds. A beta raises that to 1,800 seconds for supported runtimes.
Why does a Vercel timeout show up as a 504?
When an invocation runs past its maximum duration, Vercel stops it and returns 504 FUNCTION_INVOCATION_TIMEOUT: Gateway Timeout. Filter vercel logs output on responseStatusCode 504 to list the routes that hit it.
Does streaming time count toward maxDuration?
Yes. For request handlers, max duration includes handling the request and sending the response, streamed responses included. If clients connect over HTTP/1.1, stream heartbeat data during long idle stretches, because those connections can be closed when nothing is being sent.
Can I set every function in my project to 30 minutes?
Not yet. During the beta, durations above 800 seconds must be set per function, in code or in vercel.json, and project-level defaults above 800 seconds aren't supported. The beta covers nodejs20.x, nodejs22.x, nodejs24.x, Bun 1.x and 1.4.x, and python3.12 to python3.14, but not Secure Compute or Static IPs.
Am I billed while my function waits on a slow API?
Active CPU billing pauses while the function waits on I/O. Provisioned memory is billed for as long as the instance runs. A call that hangs until the timeout still costs memory time, so a deadline on the call saves money as well as the request.
Where do I find the logs for a timed-out request?
Open Logs in the project sidebar on the dashboard, run vercel logs --environment production --source serverless --expand, or visit the /_logs path on the deployment URL. Runtime logs are kept for 1 hour on Hobby, 1 day on Pro and 3 days on Enterprise, so check them quickly or set up a Log Drain.
Does the Edge runtime have a different timeout?
Yes. Edge runtime functions must begin sending a response within 25 seconds to keep streaming. If your work takes longer before the first byte, start streaming early or move the route to the Node.js runtime.
Will more memory stop my function timing out?
Only when the function is CPU-bound. A function with too little memory gets CPU-throttled, so raising memory from the 2 GB default can shorten heavy computation, up to 4 GB on Pro and Enterprise. It won't help a function waiting on a database or API that doesn't answer.
Sources
- FUNCTION_INVOCATION_TIMEOUT (Vercel Docs)
- How to stop Vercel Functions from timing out (Vercel Knowledge Base)
- Debugging slow Vercel Functions (Vercel Docs)
- Configuring Maximum Duration for Vercel Functions (Vercel Docs)
- Vercel Functions Limits (Vercel Docs)
- Limits (Vercel Docs)
- Vercel Function Logs (Vercel Docs)
- Polylane documentation
- Polylane full content
Boris Tane is the founder of Polylane. He previously founded Baselime, observability for the future of the cloud, which Cloudflare acquired. At Cloudflare he built and led the Workers observability team.
More in this series
- 1 How to Monitor a Django App on Render
- 2 How to Monitor a FastAPI App on Railway: Logs, Traces and Alerts
- 3 How to Monitor a Supabase App in Production
- 4 How to Debug Cloudflare Workers Errors: Logs, Traces and Error Codes
- 5 How to debug Vercel function timeouts
- 6 Vercel 504 Gateway Timeout on Serverless Functions: Causes and Fixes
- 7 Cloudflare Workers error 1101: causes and how to fix it
- 8 Cloudflare Hyperdrive connection errors: causes and fixes
- 9 How to Monitor a Convex App in Production
Related
- How to Use Claude Code to Debug Production Issues
Debug production issues with Claude Code: get logs and traces into the session, test hypotheses against evidence, stay read-only and ship a verified fix.
- How to keep AI coding agents from breaking production
Stop AI coding agents breaking production: scoped credentials, branch protection they can't bypass, required checks that block, and production-aware review.
- Fix Vercel “FUNCTION_INVOCATION_FAILED” 500 errors
Why a Vercel Function returns 500 FUNCTION_INVOCATION_FAILED, how to find the crash in runtime logs, and how to fix throws, missing env vars, files and memory.
- Fix Vercel “MIDDLEWARE_INVOCATION_FAILED” errors
Why Vercel returns 500 MIDDLEWARE_INVOCATION_FAILED or EDGE_FUNCTION_INVOCATION_FAILED, how to find the failing middleware in logs, and how to fix it.
- Fix “Durable Object reset because its code was updated”
Why a deploy makes Cloudflare Durable Objects throw “reset because its code was updated”, and how to retry safely, keep clients connected and lose no state.