AWS
故障排除

调试模式

通过设置环境变量 OPEN_NEXT_DEBUG=true 可以启用 OpenNext 的调试模式。

这将向控制台输出大量额外的日志信息。同时会禁用 esbuild 的代码压缩,并为输出添加 source map。这可能导致生成的代码体积比生产环境构建大 2-3 倍。请勿在生产环境中启用此模式

OPEN_NEXT_DEBUG=true npx open-next@latest build

无法找到 next 模块

你可能会在 CloudWatch 日志中看到这个错误:Cannot find module 'next'。这通常发生在 monorepo 项目中且存在多个锁文件的情况下。请确保项目根目录下只有一个锁文件。

减小打包体积

Next.js 可能会错误地将某些依赖包含在打包文件中。你可以通过以下 next.config.js 配置来移除它们:

outputFileTracingExcludes: { // 或 Next < 15 版本中使用 experimental.outputFileTracingExcludes
  "*": ["node_modules/the-unwanted-package"],
},

另外,除非绝对必要,否则不应将 sharp 添加为依赖项,因为图像优化功能已经自带了特定版本的 sharp。

移除 source map

Source map 可能会显著增加打包体积,从独立输出中排除它们可能很有价值。 例如,Sentry 不会删除服务器端的 source map(即使 "deleteSourcemapsAfterUpload" 设为 true),而只会删除客户端的 source map。查看 Sentry 源代码 (opens in a new tab)

你可以用类似方式排除 source map:

outputFileTracingExcludes: {
  "*": [
    './**/*.js.map',
    './**/*.mjs.map',
    './**/*.cjs.map'
  ],
},

为 ISR 修补 fetch 行为(仅适用于 next@13.5.1+ 版本)

如果在应用中使用 ISR(增量静态再生)和 fetch,可能会遇到一个导致 revalidate 值不一致的 bug。该问题的表现是:页面会使用所有 fetch 调用中最低的 revalidate 值进行重新验证,而忽略各自的设定值。要修复此问题,需要在根布局组件中按以下代码片段修改 fetch 函数:

export default function RootLayout() {
  const asyncStorage = require('next/dist/client/components/static-generation-async-storage.external');
  //@ts-ignore
  const staticStore =
    (fetch as any).__nextGetStaticStore?.() ||
    asyncStorage.staticGenerationAsyncStorage;
  const store = staticStore.getStore();
  store.isOnDemandRevalidate =
    store.isOnDemandRevalidate && !(process.env.OPEN_NEXT_ISR === 'true');
  return <>...</>;
}

页面刷新和直接访问 URL 时的路由访问拒绝错误

当刷新动态/静态路由或直接从 URL 访问类似 /profile/[userId]/[id] 的路由时,可能会收到如下 XML 格式的 Access Denied 错误:

<Error>
   <Code>AccessDenied</Code>
   <Message>Access Denied</Message>
   <RequestId>R4E6T9G2Q1S0Z5X8</RequestId>
   <HostId>S7h9F3g2T0z5K8d6A2s1W4x3C7v8B9m2L0j3K4i7H8g9F0r3A5q8w9E8r7t6Y5h4U3i2O1p0</HostId>
</Error>

在使用 App Router 时,如果通过 NextJS 的 <Link> 组件进行客户端导航也可能出现此问题。

可能的原因是 public 目录中存在与路由名称冲突的文件夹或文件。此时应重命名这些冲突的文件/文件夹。

无法找到模块 './chunks/xxxx.js' 错误

如果在 instrumentation.ts 中使用动态导入,会在运行时引发此错误。移除动态导入即可解决该问题。

Sentry 服务端配置

Sentry 文档推荐的配置在 instrumentation.ts 中使用动态导入,这会导致上述错误。

以下是可解决该问题的有效 Sentry 配置:

instrumentation.ts

import * as Sentry from "@sentry/nextjs";
import { initSentry } from "../sentry.server.config";
 
export const onRequestError = Sentry.captureRequestError;
 
export async function register() {
  initSentry(process.env.NEXT_RUNTIME as "nodejs" | "edge");
}

sentry.server.config.ts

import * as Sentry from "@sentry/nextjs";
 
export const initSentry = (runtime: "nodejs" | "edge") => {
  Sentry.init({
    dsn: "https://...",
 
    //...其余配置项
  });
};

AWS Lambda 流式响应中的空体问题

我们过去曾遇到过在 AWS Lambda 中当响应体为空时流式传输会挂起的问题。 目前 OpenNext 中有一个临时解决方案,通过设置环境变量 OPEN_NEXT_FORCE_NON_EMPTY_RESPONSEtrue。 这会在流中写入一些内容来确保它不是空的。

Yarn Plug'n'Play 清单禁止导入 "xxx" 的错误

这个错误通常可以通过删除仓库中所有 yarn 相关文件来解决。你还应该检查 package.json 中是否将 yarn 设置为 packageManager,删除该配置可以解决问题。 如果你使用 yarn,这里有一个临时解决方案 (opens in a new tab)

如果你没有使用 yarn 但看到了与 yarn 相关的错误,可以尝试运行 corepack disable 或将 nvm 更新到 0.40.2 版本。

我的打包文件中缺少某个文件/依赖项

有时您的 server functions 打包文件中可能会缺少某些文件。例如可能是 sentry.server.config.ts 文件。这可以是任何文件或目录,也支持 glob 模式匹配。在 Next.js 中有一个选项可以包含未被自动追踪到的文件,这个选项叫做 outputFileTracingIncludes。以下是在 next.config.ts 中使用它的示例:

import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  /* config options here */
  outputFileTracingIncludes: {
    "*": ["sentry.server.config.ts"],
    // 也可以使用 glob 模式
    "/api/*": ["node_modules/.prisma/client/**/*"],
  },
};
 
export default nextConfig;

这会将文件复制到 .open-next/server-functions/default/sentry.server.config.ts,或者在函数拆分的情况下复制到每个拆分函数中。要了解更多关于 outputFileTracingIncludes 的信息,可以参考 Next.js 文档 (opens in a new tab)

该功能在 OpenNext 的函数拆分场景下同样适用。如果您的键对应特定路由(例如 api/test),则该文件只会被包含在该路由的函数打包中。而使用 * 作为键时,文件会被包含在所有函数打包中。以下是函数拆分的示例:

// open-next.config.ts
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next";
const config = {
  default: {},
  functions: {
    extraFunction: {
      patterns: ["api/test"],
      // 这是该函数将使用的路由
      routes: ["app/api/test/route"],
      override: {
        wrapper: "aws-lambda-streaming",
      },
    },
  },
} satisfies OpenNextConfig;
 
// next.config.ts
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  outputFileTracingIncludes: {
    // 这些文件只会被复制到 extraFunction 的打包中
    "/api/test": ["sentry.config.ts", "node_modules/.prisma/client/**/*"],
  },
};

该功能在 monorepo 中同样有效。假设您的 Next 应用位于 packages/web 目录下,文件将被写入到:packages/web/.open-next/server-functions/default/packages/web/*