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

Next.js中tRPC客户端Mutation触发'createUnhandledError'错误求助

排查Next.js + tRPC项目中createProject Mutation的createUnhandledError错误

错误现象

实现创建项目的Mutation时,浏览器控制台抛出createUnhandledError错误,错误栈如下:

Error Stack Tracetypescript[[ << mutation #1 ]project.createProject {}Call StackcreateUnhandledErrorfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_next_dist_client_9bb038..js (744:19)handleClientErrorfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_next_dist_client_9bb038..js (905:98)errorfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_next_dist_client_9bb038..js (1036:56)[project]/node_modules/@trpc/client/dist/links/loggerLink.mjs [app-client] (ecmascript)/defaultLogger/&lt;file:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_2661d4..js (1250:15)logResultfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_2661d4..js (1286:31)errorfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_2661d4..js (1299:34)errorfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_@trpc_server_dist_072945..js (283:30)errorfile:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules@trpc_server_dist_072945..js (51:30)[project]/node_modules/@trpc/client/dist/links/httpBatchStreamLink.mjs [app-client] (ecmascript)/unstable_httpBatchStreamLink/&lt;/&lt;/&lt;/&lt;file:/D:/PROJECTS/lunaris/.next/static/chunks/node_modules_2661d4..js (990:34)

客户端代码

"use client";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { api } from "@/trpc/react";
import React from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";

type FormInput = {
  repoUrl: string;
  projectName: string;
  githubToken?: string;
};

const CreatePage = () => {
  const { register, handleSubmit, reset } = useForm<FormInput>();
  const createProject = api.project.createProject.useMutation();

  function onSubmit(data: FormInput) {
    createProject.mutate(
      {
        githubUrl: data.repoUrl,
        name: data.projectName,
        githubToken: data.githubToken,
      },
      {
        onSuccess: () => {
          toast.success("Project Created Successfully");
          reset();
        },
        onError: (error) => {
          toast.error("Failed to create project");
          console.error("Mutation Error:", error);
        },
      }
    );
    return true;
  }

  return (
    <div className="flex h-full items-center justify-center gap-12">
      <img src="/undraw_github.svg" className="h-56 w-auto" />
      <div>
        <div>
          <h1 className="text-2xl font-semibold">Link your Github Repository</h1>
          <p className="text-sm text-muted-foreground">
            Enter the URL of your repository to link it to Lunaris!
          </p>
        </div>
        <div className="h-4"></div>
        <div>
          <form onSubmit={handleSubmit(onSubmit)}>
            <Input
              {...register("projectName", { required: true })}
              placeholder="Project Name"
              required
            />
            <div className="h-2"></div>
            <Input
              {...register("repoUrl", { required: true })}
              placeholder="Github Url"
              type="url"
              required
            />
            <div className="h-2"></div>
            <Input {...register("githubToken")} placeholder="Github Token" />
            <div className="h-4"></div>
            <Button type="submit">Create Project</Button>
          </form>
        </div>
      </div>
    </div>
  );
};

export default CreatePage;

服务器端配置

Prisma Schema

generator client {
    provider = "prisma-client-js"
}

datasource db {
    provider = "postgresql"
    url      = env("DATABASE_URL")
}

model User {
    id        String   @id @default(cuid())

    createdAt DateTime @default(now())
    updatedAt DateTime @updatedAt

    imageURL  String?

    firstName      String?
    lastName       String?
    emailAddress     String   @unique

    credits    Int      @default(150)

    userToProjects UserToProject[]
}

model Project{
    id        String   @id @default(cuid())
    createdAt DateTime @default(now())      
    updatedAt DateTime @updatedAt

    name        String
    githubUrl   String

    deletedAt DateTime?

    userToProjects UserToProject[]
}

model UserToProject{
    id        String   @id @default(cuid())
    createdAt DateTime @default(now())      
    updatedAt DateTime @updatedAt

    userId    String
    projectId String

    user     User     @relation(fields: [userId], references: [id])
    project Project @relation(fields: [projectId], references: [id])

    @@unique([userId, projectId])
}

tRPC Router

import { z } from "zod";
import { createTRPCRouter, publicProcedure, protectedProcedure } from "../trpc";

export const projectRouter = createTRPCRouter({
    createProject: protectedProcedure.input(
        z.object({
            name: z.string(),
            githubUrl: z.string(),
            githubToken: z.string().optional()
        })
    ).mutation(async({ctx, input})=>{
        // const project =await ctx.db.project.create({
        //     data:{
        //         githubUrl: input.githubUrl,
        //         name: input.name,
        //         userToProjects:{
        //             create:{
        //                 userId: ctx.user.userId!,
        //             }
        //         }
        //     }
        // })
        console.log("input",input)
        return true
    })
})

复现步骤

  • 克隆仓库:
    git clone https://github.com/arghyabag/lunaris.git
    cd lunaris
    
  • 使用Bun安装依赖:
    bun install
    
  • 配置环境变量:在根目录创建.env文件,添加以下变量:
    DATABASE_URL=your_postgres_connection_string
    NEXTAUTH_SECRET=your_secret_key
    
  • 执行Prisma迁移:
    npx prisma migrate dev
    
  • 启动开发服务器:
    bun dev
    
  • 访问应用:http://localhost:3000

部署步骤

  • 全局安装Bun:
    curl -fsSL https://bun.sh/install | bash
    
  • 构建生产版本:
    bun build
    
  • 运行生产构建:
    bun start
    

排查思路与解决方法

1. 阻止表单默认提交行为

客户端onSubmit函数返回true会触发浏览器默认表单提交,导致页面刷新、tRPC请求中断,进而引发未处理错误。修改方式:

function onSubmit(data: FormInput) {
  createProject.mutate(
    {
      githubUrl: data.repoUrl,
      name: data.projectName,
      githubToken: data.githubToken,
    },
    {
      onSuccess: () => {
        toast.success("Project Created Successfully");
        reset();
      },
      onError: (error) => {
        toast.error("Failed to create project");
        console.error("Mutation Error:", error);
      },
    }
  );
  // 移除return true,或改为return false阻止默认行为
  return false;
}

也可以通过接收表单事件手动阻止:

<form onSubmit={(e) => handleSubmit((data) => onSubmit(data, e))(e)}>
function onSubmit(data: FormInput, e: React.FormEvent) {
  e.preventDefault();
  // 剩余逻辑不变
}

2. 确认认证状态与用户信息

createProject使用protectedProcedure要求用户必须登录,若未登录提交表单会触发服务器端认证错误,若错误未被正确传递会引发未处理异常:

  • 给CreatePage添加登录校验,未登录用户无法访问该页面
  • 检查tRPC上下文配置(trpc.ts中的createTRPCContext),确保ctx.user.userId从NextAuth Session中正确提取:
    export const createTRPCContext = async (opts: { req: NextRequest; res: NextResponse }) => {
      const session = await getServerSession(opts.req, opts.res, authOptions);
      return {
        db: prisma,
        user: session?.user,
      };
    };
    
  • 若NextAuth返回的用户信息无userId,需在authOptions的session回调中补充:
    session: {
      strategy: "jwt",
      callbacks: {
        session({ session, token }) {
          if (token.sub) {
            session.user.userId = token.sub;
          }
          return session;
        },
      },
    },
    

3. 处理服务器端潜在错误

即使注释了数据库操作,也要确保服务器端无未捕获错误:

import { TRPCError } from "@trpc/server";

// ... 其他代码
.mutation(async({ctx, input})=>{
  try {
    console.log("input",input)
    // 启用数据库操作前,校验用户ID存在
    if (!ctx.user?.userId) {
      throw new TRPCError({ code: "UNAUTHORIZED", message: "用户未登录" });
    }
    // const project =await ctx.db.project.create({...})
    return true
  } catch (err) {
    throw new TRPCError({ 
      code: "INTERNAL_SERVER_ERROR", 
      message: "创建项目失败",
      cause: err 
    });
  }
})

4. 检查tRPC客户端链路配置

确认tRPC客户端链路(如httpBatchStreamLink、loggerLink)配置正确,避免链路中存在未处理的错误逻辑导致异常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 13:57:02