AWS
故障排查

调试模式

可以通过设置环境变量 OPEN_NEXT_DEBUG=true 以调试模式执行 OpenNext。

这将向控制台输出 大量 额外日志。这还会禁用 esbuild 中的压缩,并向输出添加源地图。这可能导致代码大小达到生产构建的 2-3 倍。不要在生产环境中启用此功能

OPEN_NEXT_DEBUG=true npx open-next@latest build

找不到模块 next

你可能会在 CloudWatch 日志中遇到此错误:Cannot find module 'next'。很可能你处于一个 monorepo 中,并且有多个 lock 文件。只需确保在项目根目录下只有一个 lock 文件。

减小 bundle 大小

Next 可能会错误地将某些依赖项包含在 bundle 中。要移除它们,你可以在 next.config.js 中使用此配置:

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

除非绝对必要,否则也不应将 sharp 添加为依赖项,图像优化已经拥有自己的 sharp 版本。默认情况下,OpenNext 排除以下包:

  • caniuse-lite
  • sharp
  • @img
  • typescript
  • next/dist/compiled/babel
  • next/dist/compiled/babel-packages
  • next/dist/compiled/amphtml-validator
移除 sourcemaps

Source maps 可能会大幅增加 bundle 大小,将它们从 standalone 输出中排除可能是值得的。 例如,Sentry 不会移除服务器端 sourcemaps,只移除客户端的,即使 "deleteSourcemapsAfterUpload" 为 true。查看 Sentry 源代码 (opens in a new tab)

你可以类似地排除 sourcemaps:

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

修补 ISR 的 fetch 行为。仅适用于 next@13.5.1+

如果你在应用中使用 ISR 和 fetch,可能会遇到一个导致 revalidate 值不一致的 bug。 问题在于它使用页面中所有 fetch 调用的最低 revalidate 值进行重新验证,不管它们各自的值如何。要修复此 bug,你需要使用以下代码片段修改根布局组件中的 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 访问时路由上的 Access Denied 错误

如果你正在刷新动态/静态路由或直接从 URL 进入该路由。例如此路由: /profile/[userId]/[id],并且你在 XML 中收到 Access Denied 错误:

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

当客户端通过 NextJS <Link> 组件导航时,这也可能发生在 app router 中。

问题可能是你的 public 目录中有一个文件夹或文件,其名称与你的路由重叠。在这种情况下,你应该将其重命名为其他名称。

cannot find module './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_RESPONSE 设置为 true。 这将向流中写入一些内容以确保其不为空。

Yarn Plug'n'Play manifest 禁止在此处导入 "xxx",因为它未列为此包的依赖项

此错误通常通过移除仓库中的所有 yarn 文件来解决。你还应该查看 package.json,看看是否将 yarn 设置为 packageManager。移除它将解决问题。 如果你使用 yarn,有一个变通方法 在此处 (opens in a new tab)

如果你没有使用 yarn 却看到 yarn 相关错误,可以通过运行 corepack disable 或将 nvm 更新到 0.40.2 来解决。

我的 bundle 中缺少文件/依赖项

有时你的服务器函数 bundle 中可能会缺少文件。例如可能是 sentry.server.config.ts。它可以是任何文件或目录,也接受通配符。在 Next 中有一个选项可以包含未被 tracing 拾取的文件。 它叫做 outputFileTracingIncludes。以下是在 next.config.ts 中如何使用它的示例:

import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  /* 这里的配置选项 */
  outputFileTracingIncludes: {
    "*": ["sentry.server.config.ts"],
    // 也可以是通配符模式
    "/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),它将仅包含在该路由的函数 bundle 中。 然而,使用 * 作为键将其包含在每个函数 bundle 中。这是一个带有函数分割的示例:

// 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 bundle
    "/api/test": ["sentry.config.ts", "node_modules/.prisma/client/**/*"],
  },
};

它也适用于 monorepo。假设你的 Next 应用在 packages/web 中,文件将被写入:packages/web/.open-next/server-functions/default/packages/web/*