Skip to content

Fixing FUNCTION_INVOCATION_TIMEOUT on Vercel

9 min read · updated August 11, 2026

FUNCTION_INVOCATION_TIMEOUT means Vercel terminated your function because it ran past its configured maximum duration. It does not mean your code is slow. On a function that calls a model, the most common cause is a single outbound request with no deadline of its own.

The error

Vercel returns HTTP 504 with the error code FUNCTION_INVOCATION_TIMEOUT. It appears on the error page the visitor sees and in the runtime logs, and Vercel documents it on a dedicated error reference page.

The duration it exceeded is whichever of these applies: an explicit maxDuration in the route or in vercel.json, the project-level default set in the dashboard, or the platform default — which Vercel documents as 300 seconds on all plans with fluid compute enabled. Hobby cannot exceed 300 seconds; Pro and Enterprise may configure up to 800, and up to 1800 under the extended-duration beta. The full table is on the duration limits page.

Figures from Vercel’s Vercel Functions Limits page as of 11 August 2026. If you are reading a guide that says this error means you exceeded 10 seconds, it predates fluid compute becoming the default and its advice will not match what you see.

Vercel’s documented causes, reordered

Vercel lists six causes on the error page: exceeding the duration limit for your plan, not returning a response at all, an infinite loop, slow network calls, upstream errors, and unhandled exceptions that prevent a response being returned. For a function whose job is calling a model, the realistic frequency ordering is close to the reverse of the printed one.

  • A slow or hung upstream call. Node’s fetch applies no default request timeout. A provider that accepts your connection and then stalls — under rate limiting, during an incident, or on a very long generation — will hold the invocation until the platform kills it. This is the majority case and it is invisible in your code, because there is no timeout value anywhere to notice the absence of.
  • A retry loop inside an SDK. Several provider clients retry a small number of times by default, with backoff. Three attempts against a slow endpoint is a timeout assembled from individually reasonable parts, and the log shows one invocation.
  • A path that never returns. An early return missing from one branch, an await on a promise nothing resolves, a stream you opened and never closed. Vercel lists “the function must return an HTTP response; if none is returned, it will time out” as a distinct cause for exactly this reason.
  • Genuinely too much work. A long document, a large max_tokens, a reasoning model, or several calls chained in one invocation. Real, but rarer than the first two.

Diagnosing which one you have

  1. Read the runtime logs for the invocation. Vercel surfaces them in the dashboard under Logs, and documents the /_logs path on a deployment as another route to them. The recorded duration tells you whether the function ran to its ceiling — a duration equal to maxDuration within a few milliseconds is a platform kill, not a crash.
  2. Instrument the outbound call, not the invocation. Log elapsed time immediately around the fetch. If that number is within noise of the whole invocation, the model call owns the entire failure and nothing else in your handler is worth optimising.
  3. Compare p50 and p99 rather than a mean. A hang shows as a bimodal distribution — a fast cluster and a cluster pinned at the ceiling. Real work shows as a distribution that grows with input size.
  4. Check what maxDuration is actually in force. A vercel.json glob that does not match — the classic being a Next.js project using the src directory, where patterns must be prefixed with /src/ — leaves the default in place while appearing to have set something.

The fixes, in order

Give the outbound call a deadline. This is first because it converts an unexplained 504 into an error you chose:

export const maxDuration = 120;

export async function POST(request: Request) {
  const { prompt } = await request.json();
  try {
    const upstream = await fetch("https://api.openai.com/v1/responses", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + process.env.OPENAI_API_KEY,
      },
      body: JSON.stringify({ model: "gpt-4o-mini", input: prompt }),
      signal: AbortSignal.timeout(100_000),
    });
    return Response.json(await upstream.json());
  } catch (err) {
    if (err instanceof Error && err.name === "TimeoutError") {
      return Response.json({ error: "model_timeout" }, { status: 504 });
    }
    throw err;
  }
}

The abort budget sits comfortably inside maxDuration so there is time to serialise a response. A function that times out its own upstream call returns a structured error with a code your client can act on; a function that lets the platform kill it returns an HTML error page.

Then raise the duration, if the work is real. On Pro and Enterprise, up to 800 seconds generally and 1800 under the beta — which requires per-function configuration and one of the supported runtime versions, and is not available with Secure Compute or Static IPs. On Hobby there is nothing to raise.

Then change the shape. Stream, so the user sees progress and the connection stays useful; split a chain of calls into separate invocations; or, for genuinely unbounded work, use the durable-execution primitive Vercel points at rather than a larger number here.

Cron jobs and streaming edge cases

Two situations produce this error with an extra wrinkle.

Cron jobs inherit function duration limits exactly, and Vercel documents that it does not retry a failed invocation. A cron that times out is simply a run that did not happen, with no signal beyond the log. Vercel also documents that cron delivery is best-effort and can occasionally invoke the same scheduled run more than once — so the correct response to a timing-out cron is a job designed to reconcile all outstanding work since the last successful run, not one that assumes it processes exactly the last interval. See the cron jobs tutorial.

Streaming on the Edge runtime has a different clock and can fail before it reaches any duration you configured: Vercel documents that an Edge function must begin sending a response within 25 seconds, after which it may stream for up to 300. A function that thinks for thirty seconds and then produces a perfect answer fails on the first deadline, and the fix is to emit something early rather than to raise a limit.