首页 / Prisma ORM 入门教程 / 错误处理体系:错误码与重试策略

Prisma ORM 入门教程

错误处理体系:错误码与重试策略

本教程共 54 篇 · 第 42 篇 · 更新于 2026-08-11 · 约 6 分钟阅读

错误处理错误码P2002P2025P2034instanceof重试errorFormat

本节目标:看懂 Prisma 错误分几类、错误码什么意思,把错误映射成 HTTP 语义并合理重试。

错误处理做得好不好,决定用户看到的是「邮箱已注册」还是「服务器开小差了」。

错误类层次

Prisma 的异常类型挂在 Prisma 命名空间下,用 instanceof 区分:

  • PrismaClientKnownRequestError:数据库返回的已知错误,带 code 和 meta。最常见,比如唯一约束冲突 P2002。
  • PrismaClientUnknownRequestError:没有对应错误码的请求错误。
  • PrismaClientInitializationError:初始化失败,如连不上数据库、凭据错误。在 $connect() 或首条查询时抛出,带 errorCode。
  • PrismaClientValidationError:查询参数校验失败,比如缺必填字段、类型传错。
  • PrismaClientRustPanicError:引擎崩溃。v7 移除了 Rust 引擎,这个错误基本见不到了。
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient, Prisma } from "../generated/prisma/client";

const prisma = new PrismaClient({
  adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});

try {
  await prisma.user.create({ data: { email: "a@b.com" } });
} catch (e) {
  if (e instanceof Prisma.PrismaClientKnownRequestError) {
    if (e.code === "P2002") {
      console.log("邮箱已存在:", e.meta?.target);
    }
  }
  throw e;
}
Tip

Prisma 命名空间从生成的客户端导入:import { PrismaClient, Prisma } from "../generated/prisma/client"

错误码速查

P 开头的码按段分类:P1xxx 连接类,P2xxx 查询类,P3xxx 迁移类,P4xxx 内省类。日常最常遇到这几个:

错误码含义典型处理
P1001连不上数据库服务器检查地址端口,可重试
P1002连上了但超时重试
P1008操作超时重试或调大超时
P2000值超出列类型长度校验输入
P2002唯一约束冲突转 409,提示已存在
P2003外键约束失败转 400,提示关联不存在
P2015关联记录找不到转 404
P2024连接池耗尽超时降级或重试
P2025需要的记录不存在转 404
P2028事务 API 错误检查事务用法
P2034事务写冲突或死锁整体重试事务
P2037数据库连接数过多调池、加 PgBouncer

错误对象上除了 code,还有 meta:唯一约束冲突时 meta.target 给出冲突的字段名,定位问题很省事。连接类错误还有 P1017(服务器关闭连接),常见于数据库重启或网络被切断。

P2025 值得单独说:findUnique 查不到返回 null 不报错;findUniqueOrThrow、update、delete 等操作找不到记录才抛 P2025。如果「查不到」是合法场景,改用 updateMany / deleteMany,它们匹配零条也不会报错,省一层 try/catch。

ValidationError 通常在编译期就被 TypeScript 拦住了。运行时还会遇到,多半是动态输入绕过了类型检查(比如从请求体直接构造 data)。这类错误是程序 bug,不该重试,记日志后转 500。

HTTP 语义映射

API 层最常见的映射:P2002 → 409 Conflict,P2025 → 404 Not Found,P2003 → 400 Bad Request,连接类错误 → 503 Service Unavailable。集中在一个错误处理中间件里做,别散落在每个路由:

function toHttpStatus(e: unknown): number {
  if (e instanceof Prisma.PrismaClientKnownRequestError) {
    switch (e.code) {
      case "P2002":
        return 409;
      case "P2025":
      case "P2015":
        return 404;
      case "P2003":
      case "P2000":
        return 400;
      case "P2024":
      case "P1001":
        return 503;
    }
  }
  return 500;
}
Note

给用户的错误信息要克制,别把 meta、SQL 细节直接吐出去,防止泄露表结构。

自定义错误类解耦

业务层别到处依赖 Prisma 的错误码。定义自己的错误类,把 Prisma 错误翻译成业务语言,边界处再映射 HTTP:

export class EmailAlreadyExistsError extends Error {
  constructor() {
    super("Email already exists");
  }
}

服务层捕获 P2002 抛 EmailAlreadyExistsError,路由层只认自己的错误类。以后换 ORM 或错误码变化,只有翻译层要改。

错误还要记日志。用 log 选项的 error 级别加事件订阅,把错误码、模型、操作一起记下来,配合 Sentry 之类的监控按 prisma.code 打标签,事后统计哪种错误最多一目了然:

prisma.$on("error", (e) => {
  logger.error({ message: e.message, target: e.target });
});

瞬态错误重试

不是所有错误都该重试。重试只适用于瞬态错误:P1001(网络抖动)、P2024(池暂时耗尽)、P2034(写冲突或死锁,官方明确建议重试)。P2002、P2025 这类业务错误重试多少次都一样,直接抛出。

重试要带退避,别立刻连发:

async function retryOnConflict<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (e) {
      if ((e as { code?: string }).code !== "P2034" || attempt === retries) throw e;
      await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
    }
  }
  throw new Error("unreachable");
}

重试策略整体上分三类:瞬态错误重试;业务错误直接抛出并映射成 4xx;耗时操作(Saga、消息队列补偿)才考虑补偿动作。别把重试用在非幂等操作上,比如扣款,重复执行会出问题。交互式事务推荐包一层重试循环,专门对付 P2034。

errorFormat:控制错误长相

错误信息默认带颜色和排版建议,在 API 日志里很吵。三种格式:pretty(默认,彩色带修复建议)、colorless(无色)、minimal(纯文本)。构造函数里配:

const prisma = new PrismaClient({
  adapter,
  errorFormat: "minimal",
});

也可以靠环境变量:NODE_ENV=production 自动输出最小化错误,设置 NO_COLOR 去掉颜色。

最后把整套做法串成清单:用 instanceof 分层捕获 → 按错误码分流 → 业务错误映射 HTTP、瞬态错误重试 → 边界处翻译成自定义错误 → 统一记日志。按这个顺序写,错误处理就不会乱。

Note

MongoDB 相关错误(如 P2031 复制集要求)只出现在 v6,v7 不支持 MongoDB,留在 v6.19。

参考来源

  • Prisma 官方文档:Error Reference / Handling exceptions and errors / Configuring error formatting
  • Mapagam:Handling Prisma Errors
  • DevSheets:Error Handling in Prisma
  • Generalist Programmer:NestJS Prisma Tutorial Complete Guide