惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Last Week in AI
Last Week in AI
月光博客
月光博客
D
DataBreaches.Net
WordPress大学
WordPress大学
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
C
Check Point Blog
F
Fortinet All Blogs
B
Blog
小众软件
小众软件
Vercel News
Vercel News
罗磊的独立博客
有赞技术团队
有赞技术团队

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Next.js 14: 'Could not find the module in the React Clien...
박준희 · 2026-06-03 · via DEV Community

박준희

I run a small AI product on a single cheap VM, deploying it myself. One morning the homepage started throwing 500s — not always, just sometimes. The admin pages were fine. The CSS was fine. Only some routes died, and only in production.

The error in the PM2 logs was this:

Error: Could not find the module
"/tmp/riel_agent_build/src/app/page.tsx#default"
in the React Client Manifest.
This is probably a bug in the React Server Components bundler.

"Probably a bug in the bundler." It wasn't. It was me. If you're self-hosting Next.js 14 (App Router / RSC) and seeing this, here's what's actually happening — and it took me far too long to see it.

The setup that caused it

My deploy script did something that looks perfectly reasonable:

  1. Build the app in a scratch directory: /tmp/riel_agent_build
  2. Keep the old, running .next untouched during the build (zero downtime)
  3. When the build succeeds, swap just the new .next into the live app directory /home/me/app/riel_agent

Build somewhere safe, then move only the output. Classic atomic deploy. The problem is that one of those build artifacts is not relocatable.

The real cause: RSC bakes an absolute path into the client manifest

In the Next.js App Router, React Server Components need a client manifest — a map that tells the server which client module to hydrate for each "use client" boundary. In Next.js 14, the keys in that manifest are written using the absolute path of the directory the build ran in (the build CWD).

So when I built in /tmp/riel_agent_build, the manifest was full of keys like:

/tmp/riel_agent_build/src/app/page.tsx#default

Then I moved .next to /home/me/app/riel_agent and started the server from there. At runtime, Next resolves modules relative to the real CWD — /home/me/app/riel_agent/... — but the manifest is still pointing at /tmp/riel_agent_build/.... The two no longer match. For any route that crosses a server→client boundary, the lookup fails:

Could not find the module /tmp/riel_agent_build/... in the React Client Manifest.

Why "only sometimes"? Because routes with no client component (or that were statically pre-rendered) don't hit the manifest at all. Pure-static pages render fine; the moment a route needs to hydrate a client boundary at request time, it 500s. That's why my admin pages looked healthy while the homepage flickered between working and broken.

The tell is right there in the error string: it's an absolute path that is not where your app actually lives. If you ever see /home/runner/... (GitHub Actions) or /tmp/... in this error, you have the exact same disease. (I had previously hit the /home/runner version of this and "fixed" it by moving the build to /tmp — i.e. I moved the bug, not removed it.)

The fix: build in place, into a sibling output dir

The relocation was the whole problem, so the fix is to never relocate. Build with the real app directory as the CWD, and only redirect the output folder, not the working directory.

Next.js already supports this. next.config.js reads the dist dir from an env var:

// next.config.js
module.exports = {
  distDir: process.env.NEXT_DIST_DIR || ".next",
  // ...
};

So the deploy becomes:

cd /home/me/app/riel_agent           # real CWD — same as runtime

# build into a NEW folder, leaving the live .next serving traffic
rm -rf node_modules/.cache           # drop any path-polluted cache
NEXT_DIST_DIR=.next.new npx next build

# sanity-check the output before swapping (see guard below)

# atomic swap
mv .next .next.previous
mv .next.new .next
pm2 reload riel_agent

Now the manifest keys are written as /home/me/app/riel_agent/... — which is exactly where the server runs from. The paths match, the 500s stop, and I still get a zero-downtime swap because the old .next keeps serving until the very last mv.

Two details that matter:

  • Clear node_modules/.cache. Webpack/Next caches can carry the old build path forward and reintroduce the mismatch. A poisoned cache will happily rebuild the wrong absolute paths.
  • The old .next stays live during the build. Because you're building into .next.new, the running app never loses its .next. The only moment of change is the mv, which is atomic on the same filesystem.

The guard rail I added so it can never ship silently again

A deploy that produces a technically successful build but a broken manifest is the worst kind — it passes "did the build exit 0?" and still takes the site down. So I added a dumb, deterministic check before the swap: grep the new server output for any path that isn't the real app directory.

# after building into .next.new, before swapping
if grep -rqE '/tmp/|/home/runner/' .next.new/server; then
  echo "FATAL: foreign build path leaked into manifest — refusing to swap"
  exit 1
fi

If any /tmp/... or /home/runner/... string made it into the server bundle, the deploy refuses to swap and the previous build keeps running. No LLM judgment, no heuristics — just a string match for "this build was made somewhere it shouldn't have been."

The lesson

The interesting part isn't the Next.js trivia. It's that a build artifact had a hidden dependency on its own location, and my "safe" deploy strategy quietly violated it. The error blamed the bundler; the real bug was an assumption in my pipeline — "build output is relocatable" — that happened to be false for exactly one file.

When a green build still breaks production, stop trusting "it compiled" and look for the thing that's environment-specific: an absolute path, a baked-in env var, a cache. The fix is rarely more code. It's removing the assumption.


I'm building aicoreutility.com in the open — a full AI product run by one person on one small VM. Most of what I write here is the unglamorous infrastructure that broke first. This one cost me a morning of 500s.