Summary
withEve() writes the root .vercel/output/config.json from ensureEveVercelOutputConfig(), which runs at next.config load time — before Next's builder emits its own routing table into that same file. On Vercel, Next's header-conditional routes (the ones keyed on Next-Router-Segment-Prefetch) are not present in the deployed routing table.
The effect on a Next app using cacheComponents + withEve(): every segment-prefetch request is routed to /404, the router cannot assemble a soft navigation, and every client-side navigation falls back to a full document reload. PPR / App Shells are effectively inert in production.
Nothing errors. The document still serves correctly, so links appear to work — they just cost a full page load each. The only visible trace is 404s in the network tab.
- eve
0.22.6
- next
16.3.0-preview.5, with cacheComponents: true, partialPrefetching: true
Reproduction
The write is gated on process.env.VERCEL, so it reproduces locally:
VERCEL=1 next build
cat .vercel/output/config.json
{
"routes": [
{ "src": "^/eve/v1/(.*)$", "destination": { "service": "eve", "type": "service" } }
],
"version": 3,
"services": {
"eve": {
"buildCommand": "node 'node_modules/eve/bin/eve.js' build",
"framework": "eve",
"routes": [
{ "src": "^/eve/v1/(.*)$", "transforms": [{ "args": "/eve/v1/$1", "op": "set", "type": "request.path" }] }
],
"root": "."
}
}
}
Because ensureEveVercelOutputConfig() runs while the config is still loading, the file it reads does not exist yet, so it writes a complete v3 config whose routes array contains only eve's own route.
On the deployed app, with a dynamic route (/chat/[id]):
# plain RSC request — served correctly
curl -sI -o /dev/null -w '%{http_code} %header{x-matched-path}\n' \
-H 'RSC: 1' https://<app>/chat/<id>
# 200 /chat/[id].rsc
# identical request plus the segment-prefetch header
curl -sI -o /dev/null -w '%{http_code} %header{x-matched-path}\n' \
-H 'RSC: 1' -H 'Next-Router-Segment-Prefetch: /_tree' https://<app>/chat/<id>
# 404 /404
That single header is the difference between 200 and 404.
Evidence it comes from withEve()
Removing the wrapper fixes it. Changing export default withEve(nextConfig) to export default nextConfig, changing nothing else, and deploying: segment prefetch returns 200.
A control app without eve works. next-beats, the cacheComponents reference app, runs the same flags (cacheComponents, partialPrefetching, reactCompiler, typedRoutes) and sets export const prefetch = 'allow-runtime' on every page. Its segment-prefetch requests return 200 on Vercel. Its next.config.ts is a bare export default nextConfig; ours differed only by withEve().
The Next build itself is fine. next build emits the segment artifacts either way (.next/server/app/chat/[id].segments/{_tree,_shell,_full}.segment.rsc), and the app soft-navigates correctly against next start. Only the deployed Build Output routing is affected.
What we tried
Declaring the eve service in vercel.json makes ensureEveVercelOutputConfig() early-return, and VERCEL=1 next build then emits no .vercel/output/config.json at all — confirmed locally. But deployed, the entire app 404s, not just the eve routes.
That suggests the generated config is load-bearing for two things at once: registering the eve service, and keeping the Next app as the deployment's default app. Suppressing the write loses both. If that reading is right, a fix likely needs to keep emitting the default-app registration while no longer racing the framework builder for the routes array — for example by merging into the config after the builder has written it, rather than writing first at config-load time.
So as far as we can tell there is no userland workaround short of dropping withEve(), which means dropping the mounted agent.
Happy to test a patch against this reproduction if that would help.
Summary
withEve()writes the root.vercel/output/config.jsonfromensureEveVercelOutputConfig(), which runs atnext.configload time — before Next's builder emits its own routing table into that same file. On Vercel, Next's header-conditional routes (the ones keyed onNext-Router-Segment-Prefetch) are not present in the deployed routing table.The effect on a Next app using
cacheComponents+withEve(): every segment-prefetch request is routed to/404, the router cannot assemble a soft navigation, and every client-side navigation falls back to a full document reload. PPR / App Shells are effectively inert in production.Nothing errors. The document still serves correctly, so links appear to work — they just cost a full page load each. The only visible trace is 404s in the network tab.
0.22.616.3.0-preview.5, withcacheComponents: true,partialPrefetching: trueReproduction
The write is gated on
process.env.VERCEL, so it reproduces locally:{ "routes": [ { "src": "^/eve/v1/(.*)$", "destination": { "service": "eve", "type": "service" } } ], "version": 3, "services": { "eve": { "buildCommand": "node 'node_modules/eve/bin/eve.js' build", "framework": "eve", "routes": [ { "src": "^/eve/v1/(.*)$", "transforms": [{ "args": "/eve/v1/$1", "op": "set", "type": "request.path" }] } ], "root": "." } } }Because
ensureEveVercelOutputConfig()runs while the config is still loading, the file it reads does not exist yet, so it writes a complete v3 config whoseroutesarray contains only eve's own route.On the deployed app, with a dynamic route (
/chat/[id]):That single header is the difference between 200 and 404.
Evidence it comes from
withEve()Removing the wrapper fixes it. Changing
export default withEve(nextConfig)toexport default nextConfig, changing nothing else, and deploying: segment prefetch returns 200.A control app without eve works.
next-beats, thecacheComponentsreference app, runs the same flags (cacheComponents,partialPrefetching,reactCompiler,typedRoutes) and setsexport const prefetch = 'allow-runtime'on every page. Its segment-prefetch requests return 200 on Vercel. Itsnext.config.tsis a bareexport default nextConfig; ours differed only bywithEve().The Next build itself is fine.
next buildemits the segment artifacts either way (.next/server/app/chat/[id].segments/{_tree,_shell,_full}.segment.rsc), and the app soft-navigates correctly againstnext start. Only the deployed Build Output routing is affected.What we tried
Declaring the eve service in
vercel.jsonmakesensureEveVercelOutputConfig()early-return, andVERCEL=1 next buildthen emits no.vercel/output/config.jsonat all — confirmed locally. But deployed, the entire app 404s, not just the eve routes.That suggests the generated config is load-bearing for two things at once: registering the eve service, and keeping the Next app as the deployment's default app. Suppressing the write loses both. If that reading is right, a fix likely needs to keep emitting the default-app registration while no longer racing the framework builder for the
routesarray — for example by merging into the config after the builder has written it, rather than writing first at config-load time.So as far as we can tell there is no userland workaround short of dropping
withEve(), which means dropping the mounted agent.Happy to test a patch against this reproduction if that would help.