Fix this error · Servers and hosting
Fix: Vercel 500 INTERNAL_SERVER_ERROR FUNCTION_INVOCATION_FAILED
Find the real error behind Vercel's 500 in runtime logs, then fix missing env vars, unhandled errors, missing files and memory, with code.
Your Vercel Function crashed or threw an error that nothing handled, so Vercel answered with a 500. The real error is in the function's runtime logs. Most often it is a missing environment variable, an unhandled exception or promise rejection, or a file or module that did not make it into the deployed bundle.
| Fact | Detail |
|---|---|
| Error | 500: INTERNAL_SERVER_ERROR Code: FUNCTION_INVOCATION_FAILED |
| Where you see it | The browser or API client, the Vercel Logs page, vercel logs |
| Usual cause | An exception that nothing caught, often from a missing environment variable |
| Usual fix | Read the stack in the runtime logs, then fix or catch that error |
| BugTape catches it | Yes, with the Node SDK: uncaught exceptions, unhandled rejections and Next.js request errors on the Node runtime |
What the error means
Vercel's docs say this error "occurs when a function invocation fails. This could be due to an error within the function itself, or an issue with the environment in which the function is running." The status is 500 and the name is Internal Server Error. The page the user sees tells you nothing about the cause. The stack trace is in the runtime logs.
Next.js catches most errors thrown while it renders a page or runs a route handler and answers with its own 500 response. Those errors show up in the logs with their stack too. FUNCTION_INVOCATION_FAILED usually means the failure happened outside that path: while the function was starting, in a callback or promise that nobody awaited, or because the process ran out of memory or exited.
With Fluid compute, an uncaught exception or unhandled rejection in Node.js is logged, current requests on the same instance are allowed to finish, and then the process stops.
Common causes
- A missing or wrong environment variable. The variable is set locally in
.envbut not in the Vercel project, is set only for Preview and not Production, or was added after the last deploy. Vercel applies environment variable changes only to new deployments. - An unhandled exception or promise rejection. A
throwinside a callback, an event handler or a promise that no one awaits. - Code that throws when the module loads. A client created at the top of a file with a missing key, or a JSON config parsed at import time.
- A file or module missing from the bundle. A file read with
fsat runtime that the build did not trace, or a native package built for a different platform. - Running out of memory. Loading a large file or result set into memory. The memory limit is 2 GB on Hobby and up to 4 GB on Pro and Enterprise.
process.exit()in server code.
How to fix it
Read the runtime log for the failed request
Open your project's Logs page in the Vercel dashboard and filter by the failing path and the error level, or use the CLI:
vercel logs runtime --environment production --query 'level:error' --since 1hThe first stack trace for the failing request names the file and line. Fix that error. The rest of this page covers the usual ones.
Check environment variables, then redeploy
List what the deployment has, compare it with what your code reads, and redeploy after any change:
vercel env ls productionFail with a clear message when a required variable is missing, so the log tells you which one:
// lib/env.ts
export function requireEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing environment variable ${name}`);
return value;
}Remember that NEXT_PUBLIC_ variables are inlined into the browser bundle when you run next build. Changing one in the dashboard has no effect until the next build.
Create clients inside a function
A client created at the top of a module throws when the module loads, which crashes every request to that function. Create it on first use instead:
// lib/stripe.ts
import Stripe from 'stripe';
import { requireEnv } from './env';
let client: Stripe | undefined;
export function stripe(): Stripe {
client ??= new Stripe(requireEnv('STRIPE_SECRET_KEY'));
return client;
}Await your promises and catch errors
A promise that rejects with no handler crashes the Node process. Await it, or attach a .catch():
export async function POST(request: Request) {
const order = await request.json();
// Before: sendWebhook(order); // rejection has no handler
await sendWebhook(order); // the error now reaches your try/catch or Next.js
return Response.json({ ok: true });
}To run work after the response, use after() from next/server and catch inside it. See unhandled promise rejection for more.
Include files the function reads at runtime
If the log shows ENOENT or Cannot find module, the file was not included in the function. In Next.js, add it with outputFileTracingIncludes:
// next.config.js
module.exports = {
outputFileTracingIncludes: {
'/api/report': ['./templates/**/*'],
},
};Outside Next.js, use includeFiles for the function in vercel.json.
Use less memory
Stream large files and query results instead of loading them whole, and page through big tables. Pro and Enterprise teams can raise the function's memory in Settings → Functions.
How to catch it in production with BugTape
On the server. Initialise the Node SDK in instrumentation.ts. Next.js 15 and later call onRequestError for errors in pages, route handlers, Server Actions and middleware, and BugTape reports each one with its route pattern, such as /api/orders/[id]. The SDK also reports uncaught exceptions and unhandled rejections on its own.
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') (await import('@bugtape/node')).init({ service: 'web' });
}
export async function onRequestError(...args: Parameters<typeof import('@bugtape/node').captureRequestError>) {
if (process.env.NEXT_RUNTIME === 'nodejs') await (await import('@bugtape/node')).captureRequestError(...args);
}The capture key comes from the BUGTAPE_KEY environment variable. captureRequestError waits up to 2 seconds so the report is sent before the function is frozen. The Edge runtime is not supported. An error thrown before register() has run is not reported, and neither is a crash from running out of memory, because the process stops without running any code.
For a plain Vercel function outside Next.js, or a step you want to report and handle yourself, catch the error and send it before you answer:
import * as bugtape from '@bugtape/node';
bugtape.init({ service: 'api' });
export async function POST(request: Request) {
try {
return Response.json(await handle(request));
} catch (err) {
bugtape.captureException(err);
await bugtape.flush(2000);
return Response.json({ error: 'internal_error' }, { status: 500 });
}
}In the browser. The browser SDK reports the 500 as a failed request when it follows a click within 8 seconds, with the click, the page, the network log and console output. A request that is not tied to a click is recorded in the network log but does not start a report.
Repeats of the same error group into one issue with a count. On Vercel, the SDK reads VERCEL_GIT_COMMIT_SHA as the release, so when you mark an issue fixed, a report from a newer deploy reopens it as a regression. Your coding agent can read the issue over MCP (npx -y bugtape connect claude, or cursor or codex) and propose a fix. You review the pull request. Server reports have no replay or console output.
FAQ
Where do I find the real error behind FUNCTION_INVOCATION_FAILED?
In the runtime logs: the Logs page of your project in the Vercel dashboard, or vercel logs in the CLI. Filter by the failing path and the error level. The stack trace there names the file and line.
Why does it work locally but fail on Vercel?
The usual reasons are an environment variable that exists only in your local .env, a file the build did not include in the function, or a different Node.js version. Check the variables for the Production environment, redeploy after changing them, and look for ENOENT or Cannot find module in the log.
I added the environment variable and it still fails. Why?
Vercel applies environment variable changes only to new deployments. Redeploy. If the variable starts with NEXT_PUBLIC_, it is inlined at build time, so it also needs a new build.
Is FUNCTION_INVOCATION_FAILED the same as FUNCTION_INVOCATION_TIMEOUT?
No. FUNCTION_INVOCATION_FAILED is a 500 for a function that crashed or threw. FUNCTION_INVOCATION_TIMEOUT is a 504 for a function that ran past its maximum duration. See FUNCTION_INVOCATION_TIMEOUT.
Sources
- Vercel: FUNCTION_INVOCATION_FAILED, checked 2 Nov 2026.
- Vercel: Fluid compute, isolation boundaries, checked 2 Nov 2026.
- Vercel: Functions limits (memory, bundle size,
includeFiles), checked 2 Nov 2026. - Vercel: Environment variables and vercel logs, checked 2 Nov 2026.
- Next.js: Environment variables and onRequestError, checked 2 Nov 2026.
Related errors
- Vercel FUNCTION_INVOCATION_TIMEOUT:
504: GATEWAY_TIMEOUT Code: FUNCTION_INVOCATION_TIMEOUT - Node.js unhandled promise rejection:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch() - Next.js Server Components render error:
An error occurred in the Server Components render. The specific message is omitted in production builds - supabaseUrl is required:
supabaseUrl is required. - Postgres too many clients already:
FATAL: sorry, too many clients already
All guides: Fix an error.
Start free. Free plan · No credit card · Set up in minutes. Or try the live demo first.