部署成功、build log 全绿,但打开线上页面或调 API 返回 500: INTERNAL_SERVER_ERROR,下方还带一行 FUNCTION_INVOCATION_FAILED 或 EDGE_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 system 或 is 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-sdk 或 firebase-admin 当 dep 引入,但其实只用了一个子模块。
如何判断:
vercel inspect <deployment-url>
# 看 Function 大小那一列;> 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 Runtime 或 not 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 (
fs、path、crypto、net) - 部署后跑一次 health-check 脚本,curl 关键 API 端点确认 200
- 数据库一律走 pooler URL;监控 active connections 设告警阈值 80%
- function bundle 体积加 CI 守护(> 30MB 警告,> 50MB fail)