next-on-pages 突然构建失败:Vercel CLI 默认启用了 Next.js Build Adapter
代码没动,Cloudflare Pages 上的 next-on-pages 构建却突然报 Edge Runtime 错误。根因是 Vercel CLI 对 Next.js 16 默认走了新的 Build Adapter,附排查过程与修复方式。
一个部署在 Cloudflare Pages 上的 Next.js 16 站点,上周还能正常构建,这周代码和 lockfile 都没怎么动,构建却直接失败。问题不在项目本身,而在 @cloudflare/next-on-pages 内部调用的 Vercel CLI:它不锁版本,而新版 Vercel CLI 对 Next.js ≥ 16.2 默认改走 Next.js Build Adapter,产物结构变了,已归档的 next-on-pages 认不出来。
构建命令前加上 NEXT_ENABLE_ADAPTER=0,关闭 Vercel CLI 的 adapter 流程:
NEXT_ENABLE_ADAPTER=0 npx --legacy-peer-deps @cloudflare/next-on-pages@1项目背景
- Next.js 16.3.5,App Router,页面基本都是静态或 SSG
- 唯一的动态路由是
app/og/route.tsx,声明了export const runtime = 'edge',用ImageResponse生成 OG 图 - Cloudflare Pages 配置:
- 构建命令:
npx --legacy-peer-deps @cloudflare/next-on-pages@1 - 构建输出:
.vercel/output/static
- 构建命令:
站点必须留在 Pages:迁到 Workers 后,自动分配的域名会从 *.pages.dev 变成 *.workers.dev,而且手上还有很多项目处境相同。
现象
构建日志最后报错:
⚡️ ERROR: Failed to produce a Cloudflare Pages build from the project.
⚡️
⚡️ The following routes were not configured to run with the Edge Runtime:
⚡️ - /_global-error
⚡️ - /_not-found
⚡️ - /blocks/[category]
⚡️ - /docs/[id]
⚡️ - /docs/introduction.segments/_full.segment
⚡️ ...
⚡️ Please make sure that all your non-static routes export the following edge runtime route segment config:
⚡️ export const runtime = 'edge';奇怪的是,列表里的路由在 next build 的输出中全是 ○(静态)或 ●(SSG),只有 /og 是 ƒ(动态)。为什么 next-on-pages 会把它们当成没声明 Edge 的动态路由?
排查:对比两次构建日志
| 成功(09-16) | 失败(09-23) | |
|---|---|---|
| 项目依赖 | Next 16.3.5,lockfile 一致 | 相同 |
| next-on-pages | 1.13.16 | 1.13.16 |
| Vercel CLI | 59.18.0(命中缓存) | 59.25.4(npx 重新下载最新版) |
CLI 内置的 @vercel/next | 12.0.2 | 12.0.5 |
| 日志特征 | Traced Next.js server files、Created all serverless functions | Applying modifyConfig from Vercel、Running onBuildComplete from Vercel |
唯一的变化是 Vercel CLI。而失败日志里新出现的 modifyConfig、onBuildComplete,正是 Next.js Build Adapter API 的两个钩子。
根因
next-on-pages 自己并不构建 Next.js,它做两件事:
- 执行
npx vercel build,生成.vercel/output(Vercel Build Output API); - 把这份产物转换成 Pages 的
_worker.js和静态资源。
第 1 步没有锁版本,每次构建用的都是最新的 Vercel CLI。
@vercel/next 里决定是否走 adapter 的逻辑如下:
const isAdapterEnabled =
// integration tests expect outputs object
!process.env.NEXT_BUILDER_INTEGRATION &&
process.env.NEXT_ENABLE_ADAPTER === '1' &&
semver.gte(nextVersion, MINIMUM_NEXT_ADAPTER_VERSION) // 'v16.2.0-canary.28'对 Next.js ≥ 16.2.0-canary.28,NEXT_ENABLE_ADAPTER=1 已经是平台默认值。从失败日志看,新版 CLI 在本地执行 vercel build 时也启用了它。走 adapter 之后:
- SSG 页面不再输出为带 fallback HTML 的 prerender 配置(next-on-pages 能识别成静态资源),而是变成函数加新格式的 prerender 元数据(
prerenderClassification被换成了initialMetadata); experimental.collapseAdapterRoutes默认开启,多个动态路由可能共用一个路由表条目;- Next.js 16 的 segment 预取文件(
*.segments/*.segment.rsc)作为独立产物出现。
next-on-pages 已于 2025-09-29 归档,没有跟进这种格式。凡是它分辨不出是静态还是 Edge 的产物,都会被列进 “not configured to run with the Edge Runtime”。
修复
方式一:关闭 adapter(推荐)
直接写进构建命令,方便在多个项目间统一复制:
NEXT_ENABLE_ADAPTER=0 npx --legacy-peer-deps @cloudflare/next-on-pages@1也可以在 Pages 控制台配置:Workers & Pages → 项目 → Settings → Variables and Secrets,添加 Text 类型变量 NEXT_ENABLE_ADAPTER = 0,Production 和 Preview 都要加,然后在 Deployments 里 Retry deployment。
看构建日志:
- 不再出现
Applying modifyConfig from Vercel、Running onBuildComplete from Vercel - 重新出现
Created all serverless functions和 next-on-pages 的Build Summary
这个关闭方式来自 vercel/vercel#17640。
方式二:锁定 Vercel CLI 版本
pnpm add -D vercel@59.18.0next-on-pages 执行的是 npx vercel build,npx 会优先使用项目里 node_modules/.bin/vercel,不再拉最新版。和方式一一起用,可以同时防住 adapter 默认开启和其他 CLI 变动。
为什么不直接适配新格式
项目多的时候,fork 一份 next-on-pages 自己维护听起来很合理。但要支持 adapter 的产物,需要:
- 重写静态和动态的判定逻辑,识别新的 prerender 元数据;
- 拆开
collapseAdapterRoutes合并后的路由; - 把
.segments/*.segment.rsc映射成静态资源和对应的路由规则; - 追一个还在 beta 的格式:
@next-community/adapter-vercel仍是0.0.1-beta.x,在 Vercel 自己的平台上也还有 bug。
代码改动量不算大,真正的成本在于持续跟进变化。现阶段 fork 只需要做两件事:锁定 Vercel CLI 版本,默认设置 NEXT_ENABLE_ADAPTER=0。
等 Vercel 移除旧的构建流程,或者锁定的旧 CLI 不再支持新版 Next.js 时,这个开关就会失效。
留在 Pages 的长期方案
- 静态导出:站点除个别路由外都是 SSG 的话,用
output: 'export',OG 图改用opengraph-image.tsx+generateStaticParams在构建时生成,部署out/目录。不依赖适配器,也没有 Worker,域名不变。 - 静态导出 + Pages Functions:
/og?title=...这类必须在运行时处理的接口放到functions/目录,比如用workers-og或@cloudflare/pages-plugin-vercel-og。 - 面向 Pages 的 Next.js adapter:旧流程被移除后,与其适配 Vercel adapter 的产物,不如通过 Next.js 的
adapterPath写一个直接面向 Pages 的 adapter,绕过 Vercel CLI。这也是 OpenNext 正在走的方向。 - OpenNext 放进 Pages(未验证):
@opennextjs/cloudflare官方只支持 Workers。理论上可以借助 Pages 的 advanced mode(_worker.js+env.ASSETS,开启nodejs_compat)承载它的产物,但官方不支持,也没有实测过。
参考
- cloudflare/next-on-pages(已归档,官方推荐迁移到 OpenNext)
- vercel/vercel#17640:
NEXT_ENABLE_ADAPTER=0关闭方式 - vercel/next.js#98218:
collapseAdapterRoutes默认开启 - RFC: Deployment Adapters API
- nextjs/adapter-vercel
@vercel/next源码- OpenNext for Cloudflare