Vercel 500 错误常见原因

Build 过了线上 500——多半是 Serverless function、env vars 或 Edge runtime 问题。

部署成功、build log 全绿,但打开线上页面或调 API 返回 500: INTERNAL_SERVER_ERROR,下方还带一行 FUNCTION_INVOCATION_FAILEDEDGE_FUNCTION_INVOCATION_FAILED。这种”build 过了运行炸”几乎都不是代码语法问题,而是运行时上下文缺失——常见的有 env var 没同步到 production scope、Edge runtime 不支持的 Node API、冷启动超 10 秒被掐、或上游 API 没设超时拖垮整个函数。这篇按命中率给出诊断顺序,并附 vercel logs / vercel inspect 实战命令。

常见原因

按命中率从高到低:

1. Production 环境变量缺失或没勾选 scope

Preview 上跑得好好的,到 production 就 500。打开 Vercel dashboard → Settings → Environment Variables,看每条 var 后面的 scope 复选框:Production / Preview / Development 三个独立勾选。常见疏漏是新加的 OPENAI_API_KEY 只勾了 Preview,没勾 Production。

TypeError: Cannot read properties of undefined (reading 'startsWith')
  at new OpenAI (/var/task/node_modules/openai/index.js:42)

如何判断vercel env ls production 列出 production 实际能拿到的 env vars,对比代码里 process.env.XXX 的引用清单。

2. Edge runtime 用了 Node-only API

函数顶部写了 export const runtime = 'edge',但代码里 import fs from 'fs'require('crypto').createHash、或 Node 版的 Buffer。本地 dev 走的是 Node,部署后 Edge 报错。

Error: The package "fs" wasn't found on the file system but is built into node.
  at __require (file:///var/task/.../chunk.mjs:1:1)

如何判断:Function logs 里搜 wasn't found on the file systemis not supported in Edge Runtime

3. 上游 API 调用没超时

调 OpenAI / Anthropic / Stripe 这类外部 API 时没设 timeout,对方挂起 60 秒,Vercel function 默认 10 秒超时(Hobby)或 60 秒(Pro),先于上游返回挂掉。

Task timed out after 10.00 seconds
FUNCTION_INVOCATION_TIMEOUT

如何判断:function logs 看到 Task timed out + 持续时长贴近你的 plan 上限,就是超时。

4. 冷启动过慢 + 大依赖

函数 bundle > 50MB(解压后),冷启动加载耗时超过限额。常见原因:把整个 aws-sdkfirebase-admin 当 dep 引入,但其实只用了一个子模块。

如何判断

vercel inspect <deployment-url>
# 看 Function 大小那一列;&gt; 50MB 接近极限

5. Database 连接池耗尽

Serverless 每次调用都建一个新连接,PostgreSQL/MySQL 默认上限 ~100 个并发连接,几次突发流量就把池打满,后续函数全部 500。

Error: remaining connection slots are reserved for non-replication superuser connections

如何判断:DB provider 仪表盘(Supabase/Neon/PlanetScale)看 active connections 曲线是否贴着上限。

6. catch 块没抛错但也没返回 response

try { ... } catch (e) { console.error(e) }return new Response(...),函数走完没回应,Vercel 当 500 处理。

如何判断:function logs 里有错误堆栈但没有 5xx 之前的业务日志,说明逻辑没走完。

最短修复路径

Step 1:用 vercel logs 抓真实错误

Vercel UI 上的 logs 只显示最近 100 条且不能搜全文。用 CLI:

# 实时跟踪 production
vercel logs <project-name> --follow

# 抓最近 1 小时的全部 function 错误
vercel logs <project-name> --since 1h | grep -E 'ERROR|500|FUNCTION_'

或在 dashboard → Deployments → 选最新 → Functions → 点对应函数 → Logs 标签。复制完整堆栈,前两行就是真因。

Step 2:核对 env vars

# 列出 production scope 的所有 env vars(只显示 key)
vercel env ls production

# 拉到本地比对
vercel env pull .env.production
diff <(grep -oE '^[A-Z_]+=' .env.production | sort) \
     <(grep -roE 'process\.env\.[A-Z_]+' src/ | sort -u)

新加 env var 后必须 redeploy 才生效(dashboard 改完点 Redeploy)。

Step 3:Edge function 改回 Node 验证

如果错误堆栈里出现 Edge Runtimenot supported,把函数顶部改成:

// app/api/chat/route.ts
export const runtime = 'nodejs';  // 改自 'edge'
export const maxDuration = 30;    // Pro plan 可到 300

观察 24 小时,若稳定再决定要不要重构成 Edge-safe 版本(用 fetch 替代 axios、用 Web Crypto 替代 crypto)。

Step 4:所有上游 fetch 加超时

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);

try {
  const res = await fetch('https://api.openai.com/v1/...', {
    signal: controller.signal,
  });
  return Response.json(await res.json());
} catch (e) {
  if (e.name === 'AbortError') {
    return new Response('Upstream timeout', { status: 504 });
  }
  console.error(e);
  return new Response('Internal error', { status: 500 });
} finally {
  clearTimeout(timeout);
}

关键:catch 里一定 return,别只 console.error

Step 5:数据库用连接池中间件

Serverless 不要直连 Postgres。用 Prisma Data Proxy、Neon 的 @neondatabase/serverless 或 Supabase 的 pooler URL:

// 使用 pgBouncer pooler,端口 6543 而非 5432
import { Pool } from 'pg';
const pool = new Pool({
  connectionString: process.env.DATABASE_URL_POOLER,
  max: 1,  // serverless 每实例只用 1 个连接
});

预防建议

  • CI 里跑 env vars lint:grep 出代码里所有 process.env.XXX,对照 vercel env ls production,缺一个就 fail
  • 所有上游 API 调用强制 8 秒超时,catch 块强制 return Response
  • Edge function 的 PR 加 lint:检查不允许的 import (fspathcryptonet)
  • 部署后跑一次 health-check 脚本,curl 关键 API 端点确认 200
  • 数据库一律走 pooler URL;监控 active connections 设告警阈值 80%
  • function bundle 体积加 CI 守护(> 30MB 警告,> 50MB fail)

相关阅读

标签: #部署 / 托管 #排查 #排查 #Vercel