Vercel生产环境Prisma相关Runtime错误排查求助
问题
我开发了一款基于Next.js的简易Web应用,功能包括:
- 用户上传图片至S3存储桶
- 通过Prisma在Railway.app的MySQL数据库中创建条目
- 后续通过AWS Cloudfront分发图片,并在表单下方列表展示
本地开发时曾遇到OSS密钥无法从环境变量读取的问题,最终通过Node.js的FS模块读取本地文件解决,核心代码如下:
import { getSignedUrl as getSignedCloudFrontUrl } from '@aws-sdk/cloudfront-signer'; import fs from 'fs'; export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { const images = (await prisma.image.findMany({ orderBy: { created: 'desc' }, })) as ModdedImage[]; for (const image of images) { const getFileInfo = (filePath: string) => { let pemKey: string = ''; return new Promise((resolve, reject) => { const reader = fs.createReadStream(filePath); reader.on('error', (error) => { reject('There was an error'); }); reader.on('data', (chunk) => { pemKey = chunk.toString(); resolve(pemKey); }); }); }; const pemKey = await getFileInfo('private_key.pem'); const cfUrl = `https://[MY_CF_ID].cloudfront.net/${image.id}`; const url = getSignedCloudFrontUrl({ url: cfUrl, dateLessThan: '2022-12-31', privateKey: process.env.NODE_ENV === 'production' ? process.env.CF_PK! : (pemKey as string), keyPairId: process.env.CF_KPID!, }); image.url = url; } res.setHeader( 'Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59' ); return { props: { images: JSON.parse(JSON.stringify(images)), }, }; };
此外,还有一个图片API端点,通过以下代码获取最新上传的图片:
const newImage = (await prisma.image.findFirstOrThrow({ orderBy: { created: 'desc' }, })) as ModdedImage;
将代码部署到Vercel后,生产环境出现500 Runtime错误,日志如下:
[GET] / 18:00:50:89 Function Status: None Edge Status: 500 Duration: 1960.26 ms Init Duration: N/A Memory Used: 71 MB ID: arn1:arn1::2dznk-1663776050199-d3afad9777a1 User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 RequestId: 8c5d596c-f5e7-423c-b443-bc7570bb0bcd Error: Runtime exited with error: exit status 1 Runtime.ExitError prisma:info Starting a mysql pool with 5 connections. 2022-09-21T16:00:52.876Z 425dbfea-bafe-4f70-92b5-f8f335eed4ec ERROR Error: There was an error at Object.getProperError (/var/task/node_modules/next/dist/lib/is-error.js:25:12) at NextNodeServer.renderToResponse (/var/task/node_modules/next/dist/server/base-server.js:919:39) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async NextNodeServer.pipe (/var/task/node_modules/next/dist/server/base-server.js:368:25) at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:727:21) at async Router.execute (/var/task/node_modules/next/dist/server/router.js:247:36) at async NextNodeServer.run (/var/task/node_modules/next/dist/server/base-server.js:346:29) at async NextNodeServer.handleRequest (/var/task/node_modules/next/dist/server/base-server.js:284:20) at async Server.module.exports (/var/task/___next_launcher.cjs:29:9) { page: '/' } RequestId: 425dbfea-bafe-4f70-92b5-f8f335eed4ec Error: Runtime exited with error: exit status 1 Runtime.ExitError [GET] / 18:00:38:01 Function Status: None Edge Status: 500 Duration: 2019.04 ms Init Duration: 493.62 ms Memory Used: 122 MB ID: sfo1:sfo1::4hzrz-1663776037882-738e1f3a51a0 User Agent: node-fetch prisma:info Starting a mysql pool with 5 connections. 2022-09-21T16:00:41.490Z 044f8012-9805-4466-a661-8687f661257a ERROR Error: There was an error at Object.getProperError (/var/task/node_modules/next/dist/lib/is-error.js:25:12) at NextNodeServer.renderToResponse (/var/task/node_modules/next/dist/server/base-server.js:919:39) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async NextNodeServer.pipe (/var/task/node_modules/next/dist/server/base-server.js:368:25) at async Object.fn (/var/task/node_modules/next/dist/server/next-server.js:727:21) at async Router.execute (/var/task/node_modules/next/dist/server/router.js:247:36) at async NextNodeServer.run (/var/task/node_modules/next/dist/server/base-server.js:346:29) at async NextNodeServer.handleRequest (/var/task/node_modules/next/dist/server/base-server.js:284:20) at async Server.module.exports (/var/task/___next_launcher.cjs:29:9) { page: '/' } RequestId: 044f8012-9805-4466-a661-8687f661257a Error: Runtime exited with error: exit status 1 Runtime.ExitError
有时日志中不会出现prisma:info Starting a mysql pool with 5 connections.,起初怀疑Prisma问题,开发环境曾收到There are already 10 instances of Prisma Client actively running警告,已重构PrismaClient代码如下:
import { PrismaClient } from '@prisma/client'; declare global { var prisma: PrismaClient | undefined; } const prisma = global.prisma || new PrismaClient({ log: [ { emit: 'event', level: 'query' }, { emit: 'event', level: 'info' }, { emit: 'event', level: 'error' }, { emit: 'event', level: 'warn' }, ], }); if (process.env.NODE_ENV !== 'production') global.prisma = prisma; export default prisma;
请问该错误的原因是什么?如何解决?
原因分析
- 核心错误:生产环境文件读取失败
日志中的Error: There was an error直接来自getFileInfo函数的reject,说明生产环境中尝试读取private_key.pem文件时出错。Vercel部署环境中,该文件可能未被正确包含进部署包,或者路径指向错误。 - Prisma连接(次要排查点)
部分日志出现Prisma连接池启动信息,但如果数据库连接字符串未在Vercel正确配置,也可能触发应用崩溃。不过当前错误栈的源头是文件读取,优先级低于文件问题。 - 代码逻辑冗余
循环内重复定义getFileInfo函数并读取文件,既影响性能,也增加了出错概率。
解决方案
- 修复生产环境私钥获取逻辑
生产环境直接使用环境变量CF_PK,确保:- 在Vercel控制台正确配置
CF_PK环境变量,私钥内容需完整保留格式(换行符可用\n替代) - 移除生产环境中读取本地文件的分支,避免不必要的文件IO操作
- 在Vercel控制台正确配置
- 优化本地文件读取逻辑
将getFileInfo函数移到循环外,且仅读取一次文件:export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { let pemKey = ''; if (process.env.NODE_ENV !== 'production') { const getFileInfo = (filePath: string) => { return new Promise<string>((resolve, reject) => { let key = ''; const reader = fs.createReadStream(filePath); reader.on('error', (error) => { reject(`读取文件失败: ${error.message}`); }); reader.on('data', (chunk) => { key += chunk.toString(); }); reader.on('end', () => { resolve(key); }); }); }; pemKey = await getFileInfo('private_key.pem'); } const images = (await prisma.image.findMany({ orderBy: { created: 'desc' }, })) as ModdedImage[]; for (const image of images) { const cfUrl = `https://[MY_CF_ID].cloudfront.net/${image.id}`; const url = getSignedCloudFrontUrl({ url: cfUrl, dateLessThan: '2022-12-31', privateKey: process.env.NODE_ENV === 'production' ? process.env.CF_PK! : pemKey, keyPairId: process.env.CF_KPID!, }); image.url = url; } res.setHeader( 'Cache-Control', 'public, s-maxage=10, stale-while-revalidate=59' ); return { props: { images: JSON.parse(JSON.stringify(images)), }, }; }; - 验证Prisma环境配置
确认Vercel中已正确配置数据库连接字符串(DATABASE_URL),确保Prisma能正常连接Railway的MySQL实例 - 增加全局错误捕获
在getServerSideProps中添加try-catch,捕获并输出详细错误信息,便于后续排查:export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { try { // 原有业务代码 } catch (error) { console.error('getServerSideProps执行失败:', error); return { props: { images: [], }, }; } };
内容的提问来源于stack exchange,提问作者viktorfromearth
相关产品推荐
相关产品推荐

