You let Claude Code run a refactor for 30 minutes. You come back, run npm run build, and get a wall of red: Cannot find name 'XYZ', Module has no exported member 'foo', vite.config.ts:12 Unexpected token. The agent confidently reports “fixed and tested,” but the build never lies. “AI cleaned things up and now the build is broken” is one of the most common incidents in agent-assisted dev. Most of the time it touched a config file it shouldn’t have, added a non-existent import, or left dev-time mocks behind.
Fastest fix (90% of cases): Run npx tsc --noEmit to see every error at once (not just the first one npm run build stops on), then git restore --source=HEAD~1 -- <file> the highest-risk changed files — config files first, then shared lib/utils. Re-build after each group. This bisects you to the bad change in about 10 minutes without a full git reset. The rest of this guide is the detailed flow plus how to stop it recurring.
Common causes
Ordered by hit rate, highest first.
1. Agent edited a config file (tsconfig / vite.config / next.config)
The most common and most painful. Agent sees a type error and loosens tsconfig.json strict, or rewrites resolve.alias in vite.config.ts — a single local fix cascades into a global build failure.
error TS5023: Unknown compiler option 'allowImportingTsExtensions'.
error during build:
RollupError: "default" is not exported by "src/utils/helpers"
How to spot it: git diff main -- '*.config.*' tsconfig*.json shows whether any config file was touched.
2. Imports that don’t resolve
Agent invents a utility path (@/utils/superhelper, ./lib/notFound), TypeScript can’t find it:
src/components/Form.tsx:5:18 - error TS2307: Cannot find module
'@/utils/superhelper' or its corresponding type declarations.
How to spot it: tsc --noEmit lists every TS2307 / TS2305.
3. Test mocks left behind
Agent added vi.mock('next/router') during debugging or stuffed experimental: { mock: true } into next.config.js and forgot to remove it.
Error: <Link> requires an href prop
How to spot it: Grep the diff for mock, stub, fixture, TODO, XXX.
4. Deleted an export used by other files
Agent thought a function was unused and deleted it. 5 other files still import it:
src/pages/index.tsx:8:10 - error TS2305: Module './lib/auth'
has no exported member 'verifyToken'.
How to spot it: has no exported member or cannot find name in the build error.
5. Incompatible package version (API breaking)
Agent bumped a major version (React 18 → 19, Next.js 14 → 15) but the code still uses the old API:
TypeError: ReactDOM.render is not a function
How to spot it: git diff main -- package.json for major version jumps.
6. ESM / CJS mismatch
Agent wrote require() in an ESM project or top-level import in a CJS project:
SyntaxError: Cannot use import statement outside a module
How to spot it: Check "type": "module" in package.json against the import/require syntax used.
Shortest path to fix
Ordered by ROI. Steps 1-3 locate 90% of failures.
Step 1: Stash first, keep a clean comparison point
Don’t touch code yet. Snapshot first:
git stash push -m "ai-broken-build-snapshot" # save current state
git stash apply # re-apply, don't drop stash
You can always git stash pop back to the agent’s output. If the changes are already committed:
git tag broken-build-snapshot # tag current commit
Step 2: List changed files with git diff, sort by risk
git diff main --stat
Triage in this order (risk descending):
| Priority | File type | Risk |
|---|---|---|
| 1 | tsconfig*.json *.config.{js,ts,mjs} | One edit breaks everything |
| 2 | package.json package-lock.json | Dep incompatibility |
| 3 | src/lib/** src/utils/** | Imported by many files |
| 4 | src/components/** | Bounded blast radius |
| 5 | src/pages/** src/app/** | Single page fails |
Step 3: Run tsc --noEmit to surface every type error
Don’t go straight to npm run build; the bundler usually stops at the first error. tsc --noEmit type-checks the whole project without emitting JS, so it reports every error in one pass:
npx tsc --noEmit --pretty | head -50
--pretty adds the offending source line and color so you can scan fast. This lets you tell whether it’s one cascading change or several independent ones. If every error clusters on imports from a single file, you’ve already located the culprit. Note: tsc runs on the whole project, so it can’t be pointed at one file — but the file list in the output is your map.
Step 4: Selectively revert the high-risk files
Use git restore for file-level reverts. As of 2026 it’s the recommended command over the older git checkout -- <file> syntax (Git split the two jobs out: git switch for branches, git restore for files, so intent is unambiguous and you can’t accidentally switch branch). --source picks where to restore from — main, a commit SHA, or HEAD~1 if the agent’s work is the last commit. Try config files first:
git restore --source=main -- tsconfig.json vite.config.ts next.config.js
npm run build
(Older Git, or muscle memory: git checkout main -- tsconfig.json vite.config.ts next.config.js does the same thing.)
If build passes, the issue was config. If it still fails, restore the config changes back (git stash pop, or re-apply the agent’s version) and revert the next group:
git restore --source=main -- src/lib/ src/utils/
npm run build
Bisecting beats a full reset: you keep the agent’s good edits and only roll back the bad group. Build after each revert group until it’s green.
Step 5: Feed the root cause back into the agent
Once you know which files / lines broke things, don’t hand-fix — give the agent a precise prompt:
Your last change broke the build. Exact errors:
[paste full tsc --noEmit output]
Do only this:
1. Do not touch tsconfig.json / vite.config.ts.
2. Fix every type error above with the smallest possible edit per error.
3. Run tsc --noEmit until green before saying "done."
Explicitly forbidding config edits prevents the repeat.
How to confirm it’s fixed
Don’t trust “build passed” alone after a targeted revert. Run all three:
npx tsc --noEmit # 0 type errors
npm run build # exits 0, dist/ written
git diff --stat main # the only changed files are the ones you intended to keep
The third check matters most: it proves you didn’t quietly lose a good agent edit when you reverted a group. If a file you wanted is missing from the diff, re-apply just that file (git restore --source=stash@{0} -- path/to/file) and re-build.
Prevention
- In your agent rules file, explicitly forbid edits to config files and list exact paths:
tsconfig.json,vite.config.*,next.config.*,astro.config.*,package.json(except vianpm install). As of 2026,AGENTS.mdis the cross-tool standard read natively by Claude Code, Cursor, and Codex CLI;CLAUDE.mdand.cursorrulesstill work as tool-specific overrides. - Pre-commit hook (Husky + lint-staged) runs
tsc --noEmitandnpm run build; a non-zero exit blocks the commit - Require the agent to run
npm run build(not justnpm test) before declaring “done” — the test runner often passes while the production bundle fails - Break long refactors into small commits; each commit must build independently, so a bad one is a one-line
git restore - Review config-file changes separately; never bundle them into a feature PR
- Use
git worktreeso the agent works in an isolated branch andmainstays always-buildable
FAQ
The dev server runs fine but npm run build fails. Why?
vite dev / next dev are lenient: they transpile per-file on demand and skip the whole-project type check and tree-shaking. npm run build runs Rollup/Turbopack over the entire graph and a strict tsc pass, so it catches dead imports, type errors, and ESM/CJS issues the dev server never sees. A classic symptom is RollupError: "X" is not exported by "Y" at build time only — usually a type re-exported as a value, or a package whose exports field resolves differently in build. Always run the build before trusting the agent.
Should I just git reset --hard and start over?
Only as a last resort. A hard reset throws away every change, including the agent’s correct edits, and you relearn nothing about what broke. The targeted git restore flow keeps the good work and pinpoints the bad group in minutes. Reset only when the diff is tiny or you genuinely want to abandon the whole run.
git restore or git checkout for reverting one file?
Both work. git restore --source=<ref> -- <file> is the command Git recommends as of 2026 — it does only one thing (restore file contents) so it can’t accidentally switch your branch the way git checkout can. git checkout <ref> -- <file> is the older equivalent and is fine if it’s in your muscle memory.
How do I make the agent fix it without breaking config again?
Paste the full tsc --noEmit output back and constrain the task: forbid touching config files by exact path, demand the smallest edit per error, and require it to run tsc --noEmit until green before saying “done.” See the prompt in Step 5. Better still, put the config-file ban in AGENTS.md so it applies to every future run.
tsc --noEmit is green but npm run build still fails. What now?
The failure is a bundler-level issue, not a type issue: an unresolved runtime import, a missing asset, an env var read at build time, or an ESM/CJS boundary. Read the build error directly (it names the file and importer), and check git diff main -- package.json '*.config.*' first — version bumps and config edits are the usual cause of build-only failures.
Related
- How to roll back AI code changes
- AI hallucinated a file that doesn’t exist
- Claude Code edited the wrong file
- AI pre-commit review workflow
- Claude Code SEO audit
- AI dependency upgrade workflow
- AI Agent Loops Without Making Progress
Tags: #AI coding #Debug #Troubleshooting