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

NextJS报错TypeError: Cannot read 'replace' of undefined求助

调试 TypeError: Cannot read properties of undefined (reading 'replace') 问题

出现错误:Server Error TypeError: Cannot read properties of undefined (reading 'replace'),不知道从哪入手调试。

原本系统运行正常,尝试在Cookie Token中添加用户套餐信息以设置Premium用户时失败,移除middleware后系统恢复正常,求指导定位问题。

错误截图:
错误截图

middleware.ts 代码

const handleI18nRouting = createMiddleware(routing);

function detectLocale(req: NextRequest) {
  const country = req.headers.get('x-vercel-ip-country');

  if (country === 'VN') return 'vi';
  if (country === 'CN') return 'zh';

  // 本地运行时的 fallback
  const language = req.headers.get('accept-language');

  if (language?.startsWith('vi')) return 'vi';
  if (language?.startsWith('zh')) return 'zh';

  return 'en';
}

export async function middleware(req: NextRequest) {
  const path = req.nextUrl.pathname;

  // 跳过 api 和 static 文件
  if (
    path.startsWith('/api') ||
    path.startsWith('/_next') ||
    path.includes('.')
  ) {
    return NextResponse.next();
  }

  // 检查是否已有 locale
  const hasLocale = /^\/(vi|zh)(\/|$)/.test(path);
  if (!hasLocale) {
    const locale = detectLocale(req);

    // 英文对应根路径
    if (locale === 'en') {
      return handleI18nRouting(req);
    }

    const url = req.nextUrl.clone();
    url.pathname = `/${locale}${path}`;

    return NextResponse.redirect(url);
  }
  console.log('4');

  // 验证会话
  const session = await verifySession();

  const isAdminRoute = Object.values(adminUrl).some(
    p => path === p || path.startsWith(`${p}/`),
  );

  const isProtectedRoute = Object.values(protectedUrl).some(
    p => path === p || path.startsWith(`${p}/`),
  );

  const authPages = [
    '/authentication/sign-in',
    '/authentication/sign-up',
    '/authentication/forgot-password',
  ];

  const isAuthPage = authPages.includes(path);

  if (!session && (isProtectedRoute || isAdminRoute)) {
    return NextResponse.redirect(new URL('/authentication/sign-in', req.url));
  }

  if (session && isAdminRoute && !session.isAdmin) {
    return NextResponse.rewrite(new URL('/unauthorized', req.url));
  }

  if (session && isAuthPage) {
    return NextResponse.redirect(new URL('/', req.url));
  }

  return handleI18nRouting(req);
}

export const config = {
  matcher: ['/((?!api|_next|_vercel|.*\\..*).*)'],
};

verifySession.ts 代码

import { cookies } from 'next/headers';
import { cache } from 'react';
import { decrypt } from './session';
type SessionPayload = {
  userId: string;
  isAdmin: boolean;
  subscriptionTier?: string;
  subscriptionExpiredAt?: string | Date;
};

export const verifySession = cache(async () => {
  try {
    const cookie = (await cookies()).get('session')?.value;

    if (!cookie || typeof cookie !== 'string') return null;

    const session = (await decrypt(cookie)) as SessionPayload;
    if (!session?.userId) {
      return null;
    }

    const expiredAt = session.subscriptionExpiredAt
      ? new Date(session.subscriptionExpiredAt)
      : null;

    const isVip = expiredAt ? expiredAt > new Date() : false;

    return {
      isAuth: true,
      userId: session.userId,
      isAdmin: session.isAdmin,
      isVip,
      subscriptionTier: session.subscriptionTier ?? 'FREE',
      subscriptionExpiredAt: expiredAt,
    };
  } catch (error) {
    console.error('verifySession error:', error);
    return null;
  }
});

decrypt.ts 代码

export async function decrypt(token: string) {
  if (!token || typeof token !== 'string') {
    throw new Error('Invalid token input');
  }

  try {
    const { payload } = await jwtDecrypt(token, ENC_KEY);

    if (!payload?.jws || typeof payload.jws !== 'string') {
      throw new Error('Invalid token structure');
    }

    const { payload: verifiedPayload } = await jwtVerify(payload.jws, SIGN_KEY);

    return verifiedPayload;
  } catch (err) {
    console.error('DECRYPT ERROR:', err);
    throw err;
  }
}

调试建议

  1. 定位replace调用点
    错误提示是调用undefined的replace方法,你贴的代码里没直接出现,但大概率是handleI18nRouting内部逻辑,或者jwtDecrypt/jwtVerify库的内部代码触发的。可以在浏览器DevTools的错误栈里找具体的调用位置,或者在代码中逐步注释逻辑,看哪一步触发错误。

  2. 检查Token结构的兼容性
    你新增了套餐字段,要确认:

    • 旧Token(未包含新字段)是否能正常解密,会不会因为字段缺失导致后续逻辑出错
    • 新生成的Token中subscriptionExpiredAt是否是合法的ISO日期字符串,避免new Date()解析出错
  3. 加日志排查Session验证流程
    在verifySession里添加日志,打印解密后的session对象:

    const session = (await decrypt(cookie)) as SessionPayload;
    console.log('Decrypted session data:', session); // 新增这行
    

    看session是否有undefined的字段,比如isAdmin会不会变成undefined,导致后续判断逻辑异常。

  4. 逐步隔离middleware逻辑
    先注释掉const session = await verifySession();及之后的所有会话验证逻辑,看系统是否正常。如果正常,再逐步恢复每一段逻辑,比如先恢复session获取,再恢复路由判断,找到触发错误的具体代码块。

  5. 处理解密异常的边界情况
    decrypt函数抛出错误时,verifySession返回null,但要确认错误是否会传播到上层逻辑。可以在decrypt里捕获错误后返回null,而不是抛出,或者在verifySession里更细致地处理不同类型的错误。

内容的提问来源于stack exchange,提问作者Khuong Viet Tai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 14:04:51