You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

请问该错误的原因是什么?如何解决?


原因分析
  1. 核心错误:生产环境文件读取失败
    日志中的Error: There was an error直接来自getFileInfo函数的reject,说明生产环境中尝试读取private_key.pem文件时出错。Vercel部署环境中,该文件可能未被正确包含进部署包,或者路径指向错误。
  2. Prisma连接(次要排查点)
    部分日志出现Prisma连接池启动信息,但如果数据库连接字符串未在Vercel正确配置,也可能触发应用崩溃。不过当前错误栈的源头是文件读取,优先级低于文件问题。
  3. 代码逻辑冗余
    循环内重复定义getFileInfo函数并读取文件,既影响性能,也增加了出错概率。

解决方案
  1. 修复生产环境私钥获取逻辑
    生产环境直接使用环境变量CF_PK,确保:
    • 在Vercel控制台正确配置CF_PK环境变量,私钥内容需完整保留格式(换行符可用\n替代)
    • 移除生产环境中读取本地文件的分支,避免不必要的文件IO操作
  2. 优化本地文件读取逻辑
    将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)),
            },
        };
    };
    
  3. 验证Prisma环境配置
    确认Vercel中已正确配置数据库连接字符串(DATABASE_URL),确保Prisma能正常连接Railway的MySQL实例
  4. 增加全局错误捕获
    在getServerSideProps中添加try-catch,捕获并输出详细错误信息,便于后续排查:
    export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
        try {
            // 原有业务代码
        } catch (error) {
            console.error('getServerSideProps执行失败:', error);
            return {
                props: {
                    images: [],
                },
            };
        }
    };
    

内容的提问来源于stack exchange,提问作者viktorfromearth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 10:25:34