Get started Dashboard
Troubleshooting ·
Part 5 of Platform playbooks

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

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

seconds
Default (seconds) 300
Maximum (seconds) 300

Pro

seconds
Default (seconds) 300
Maximum (seconds) 800

Enterprise

seconds
Default (seconds) 300
Maximum (seconds) 800
Figure 1
Default and maximum duration for Node.js, Bun and Python functions with Fluid compute, from Vercel's limits docs

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 timeout or ECONNREFUSED in 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

  1. Confirm the 504s and find the routes. The JSON output of vercel logs includes each request’s requestPath and responseStatusCode, 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.

  1. Read the full log messages for those requests.
vercel logs --environment production --source serverless --since 1h --expand
  1. 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.

  2. Measure one endpoint. vercel httpstat breaks 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.

  1. 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).

  1. 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.

  1. 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

  1. Deploy a preview: vercel deploy.
  2. Measure the same route against it and compare with your earlier numbers:
vercel httpstat /api/slow-endpoint --deployment PREVIEW_URL
  1. Check that it still returns the right response:
vercel curl /api/slow-endpoint --deployment PREVIEW_URL
  1. 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.
  2. 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 waitUntil or 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 src directory, paths need the /src/ prefix. Fix: check the limit with vercel inspect after you deploy.
  • Streams that idle out on HTTP/1.1. For long-running handlers on HTTP/2, Vercel sends PING frames 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 maxDuration says. 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

  1. FUNCTION_INVOCATION_TIMEOUT (Vercel Docs)
  2. How to stop Vercel Functions from timing out (Vercel Knowledge Base)
  3. Debugging slow Vercel Functions (Vercel Docs)
  4. Configuring Maximum Duration for Vercel Functions (Vercel Docs)
  5. Vercel Functions Limits (Vercel Docs)
  6. Limits (Vercel Docs)
  7. Vercel Function Logs (Vercel Docs)
  8. Polylane documentation
  9. Polylane full content

About the author

Boris Tane

Founder of Polylane

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

Platform playbooks

  1. 1 How to Monitor a Django App on Render
  2. 2 How to Monitor a FastAPI App on Railway: Logs, Traces and Alerts
  3. 3 How to Monitor a Supabase App in Production
  4. 4 How to Debug Cloudflare Workers Errors: Logs, Traces and Error Codes
  5. 5 How to debug Vercel function timeouts
  6. 6 Vercel 504 Gateway Timeout on Serverless Functions: Causes and Fixes
  7. 7 Cloudflare Workers error 1101: causes and how to fix it
  8. 8 Cloudflare Hyperdrive connection errors: causes and fixes
  9. 9 How to Monitor a Convex App in Production

Related

Nobody should be on-call. Polylane watches your infra, finds what broke, and writes the fix.

Get started for free