Cursor 改完 build 挂了

Composer 说完成、改过的文件都过了 lint,但 build 失败。最快的修法:打开 Auto-run,让 agent 自己在收尾前跑 typecheck + build。

Composer 应用完编辑,碰过的文件里没有红波浪线,你跑 pnpm build 立刻挂:missing import、type 错位、Prisma client 没更新、ESM/CJS 不匹配。原因很机械,不神秘:Cursor 的 Apply 只校验它改的那一个文件局部正确,不跑全图 typecheck,不跑你的 codegen step,更不知道你的 CI 容器长什么样。于是出现”局部绿、全局红”。

最快的修法(先做这个): 让 agent 在汇报”完成”之前自己 build 一遍。在 Cursor 里打开 Settings → Agent → Auto-run(这是当前版本 Cursor 把 “Yolo mode” 改的名,截至 2026 年 6 月仍是这个叫法),用自然语言写一条 allow-list,比如”允许跑 pnpm typecheckpnpm buildpnpm testeslint”。然后给 Composer 一条规则:“After any code change, run pnpm build; if it fails, fix it and run again; only stop when it is green.”(改完代码就跑 build,失败就修了再跑,绿了才停。)下面这些故障大多会因此消失,因为 agent 现在看到的错误跟你一样,并会自己迭代。

下面这部分是当仍有一条错误漏网时的对症诊断。

你属于哪一类?

把 build 错误的第一行对照下表,再跳到对应修法。

build 错误里出现大概率原因跳到
Expected N arguments, but got M改了签名,没改全调用点原因 1 / Step 2
Cannot find module / Module not found新文件、import 路径或 alias 不对原因 2 / Step 3
路径指向 .prisma/src/generated/__generated__/codegen 没重跑原因 3 / Step 4
ERR_REQUIRE_ESM / Cannot use import statement outside a module / X is not a functionESM/CJS 不匹配原因 4 / Step 5
一个 env 变量名或 fixture 文件路径;本地过、CI 挂代码读了 CI 没有的 env / 文件原因 5 / Step 6
本地用 tsc --noEmit 复现不出来的 type 错build 用了更严的 tsconfig原因 6 / Step 6

常见原因

1. 改了函数签名漏了调用点

Cursor 把 foo(a, b) 改成 foo(a, b, c),但只改了它当时打开的 3 个调用点,仓库里其它 7 个报 Expected 3 arguments, but got 2

如何判断:跑 pnpm typecheck 再用 grep "Expected" 过滤,错都集中在某一个函数 / type 上。

2. 新建文件没加 import 或路径不对

Composer 创建了 src/utils/parseInvoice.ts,但调用方写的是 from "./utils/parse-invoice"(kebab-case)或 from "@/utils/parseInvoice"(依赖一个可能根本没配的 path alias)。本地 dev server 宽容,build 严格。

如何判断:构建错误里出现 Cannot find moduleModule not found

3. Codegen 没重跑

改了 Prisma / GraphQL / protobuf / OpenAPI schema 后,对应生成的 client 或 types 没重新生成。代码引用的新字段在生成产物里还不存在。

如何判断:错误指向 node_modules/.prisma/src/generated/__generated__/ 这类生成目录。

4. ESM / CJS 不匹配

Composer 在一个 CommonJS 项目里写了 import { foo } from "esm-only-package",或者在纯 ESM 项目里用了 require()。dev 运行器(ts-node / tsx)能容忍混用,打包器(esbuild / Vite / Rollup)会爆。

如何判断:错误带 ERR_REQUIRE_ESMCannot use import statement outside a module、或 X is not a function

5. 引用了 CI 没有的 env / 文件

新代码读 process.env.NEW_TOKEN 或某个本地 fixture 文件。本地有,CI 容器里两样都没有,build 时报 undefinedENOENT

如何判断:构建错里出现一个 env 变量名或文件路径;本地能 build,CI 挂。

6. tsconfig 多份(dev vs build)

tsconfig.json 宽松(noEmitskipLibCheck),tsconfig.build.json 严格(strictnoUnusedLocals)。Cursor 和你的编辑器按宽松那份检查,build 走严格那份。

如何判断:跑 ls tsconfig*.json;如果有多份,比较它们的 strict 设置。

动手前先确认

  • 确认 build 失败是在本地、CI、还是 production deploy。只在一处挂提示你查 env / 配置差异(对应原因 5 和 6)。
  • 复现前先 commit 一次,这样能准确判断是哪次改动挂了,也方便干净回滚。
  • 记下 Cursor 版本和当前模型(Composer 2.5、Sonnet 4.6、GPT-5.5 等)。不同模型 import 路径风格不同——有的偏好相对路径,有的偏好 path alias。

需要收集的信息

  • 完整 build 错误日志——留第一行和最后一行,中间通常是级联噪音。
  • Composer 最近一次 turn 的 prompt 加它的 diff stat。
  • tsconfig.json / tsconfig.build.json,以及 package.json 里的 build script。
  • 是否有 codegen step,上次成功跑是什么时候。
  • 本地 Node / pnpm 版本 vs CI 用的版本。

最短修复路径

按”先全图扫一遍,再按错类型对症”。

Step 1:跑完整 typecheck + build

pnpm typecheck   # 或 tsc --noEmit
pnpm build

第一条错误,不是最后一条。AI 引入的错通常级联,第一条最接近 root cause。

Step 2:签名改动让 Composer 自己扫调用点

I changed the signature of `foo` from (a, b) to (a, b, c).
List EVERY call site in the repo and update each one.
After updating, run `pnpm typecheck` and paste the result.

让 agent 自己跑 typecheck 验证,别只信它自己扫的结果。

Step 3:missing import 让模型修正路径

Build fails with "Cannot find module './utils/parse-invoice'".
The file is actually at src/utils/parseInvoice.ts (camelCase).
Update all import paths to match. Verify with tsc --noEmit.

Step 4:codegen 漂了重跑

pnpm prisma generate
pnpm codegen           # GraphQL
pnpm proto             # protobuf

把 codegen 做成常驻规则。在 Cursor 里现在的正确做法是用 project rule 文件,而不是老的 .cursorrules(它还能用,但已在逐步淘汰,见 FAQ)。新建 .cursor/rules/codegen.mdc,frontmatter 写 alwaysApply: true,正文:

After any change to a *.prisma, *.graphql, or *.proto file, run the
matching codegen command (e.g. `pnpm prisma generate`) before declaring
the task done.

Step 5:ESM/CJS 按项目实际类型修

package.json"type" 字段。"type": "module" 全用 import;缺这个字段或 "type": "commonjs" 全用 require。让 Composer 把这个文件改得跟项目其余部分一致,并记住 ESM 的相对 import 要显式带 .js 扩展(from "./foo.js",不是 from "./foo")。

Step 6:只在 build / CI 挂的查 env + tsconfig

diff tsconfig.json tsconfig.build.json
grep -A 3 "env" .github/workflows/*.yml

把严格那份 config 喂给 Composer:

We use tsconfig.build.json (strict mode) for production. Re-check your
changes under that config and fix any new errors. Run:
tsc -p tsconfig.build.json --noEmit

env 缺失的话,确认这个变量在 CI secrets 和 .env.example 里都声明了,而不是只在你本地的 .env

怎么确认已经修好

  • 本地 pnpm typecheck && pnpm build 都过。
  • push 后 CI 全绿——build 和整套测试都过,不只是 unit test 绿。
  • 在干净 checkout 上 pnpm install && pnpm build 能跑通。这能抓出 env 漂移和悄悄变动的 lockfile。
  • 往后只要开着 Auto-run,Composer 的最后一条消息应该明确说它跑过 build 并通过了。

如果还是没修好

  • 把错缩到最小:单个 import,或单条 type 报错。
  • 回滚 Composer 那条 commit,确认 build 是否恢复,再逐 hunk 重新 apply,找出闯祸的那块。
  • forum.cursor.com 搜对应错误文案;附上 tsconfigpackage.json 和日志。
  • 通过 Help → Report Issue 导出日志(可以附诊断日志),或抓 agent 输出贴到 Bug Reports 板块。

预防建议

  • CI 强制:每个 PR 跑完整 typecheck + build + 整套测试,不只 unit。
  • 打开 Settings → Agent → Auto-run,给 typecheckbuildlinttest 配 allow-list,让 Composer 自我验证。把破坏性命令(rm -rfgit push --force)排除在 allow-list 之外。
  • 加一条 project rule:.cursor/rules/verify.mdc,写 alwaysApply: true,正文 “After any code change, run pnpm build and report errors before finishing.”(你也可以在仓库根放一个纯 markdown 的 AGENTS.md。)
  • pre-commit hook:至少对 staged files 和它们的引用方跑 tsc --noEmit
  • Schema 项目(Prisma / GraphQL / Protobuf)把”改 schema 必须接 codegen”写成铁规则。
  • 让 Composer 任务以 “I ran the build and it succeeded” 收尾,不是 “I’m done”。

常见问题

为什么 Cursor 说改动是干净的,build 却明显不干净? Apply 只在编辑器的宽松上下文里对它改的那一个文件 typecheck,不跑项目级 tsc、不跑 codegen、不跑打包器,也从来看不到你的 CI 配置。修法是让 agent 真的跑一遍 build(Auto-run),这样它对”完成”的判断才和你一致。

该用 .cursorrules 还是 .cursor/rules/*.mdc 优先用 .cursor/rules/*.mdc。截至 2026 年 6 月,单文件的 .cursorrules 还能加载,但已被当作 legacy 并在逐步淘汰。.cursor/rules 目录里放受版本控制的 .mdc 文件,有四种应用模式——Always、Apply Intelligently、Apply to Specific Files(按 glob)、Manual(@rule-name)。build 验证类规则用 alwaysApply: true。仓库根放一个 AGENTS.md 是更简单的纯 markdown 替代方案,Cursor 同样会读。

Auto-run 是什么?安全吗? Auto-run(前身是 “Yolo mode”)让 agent 不必逐条确认就能跑终端命令,由你写的自然语言 allow-list 把关。对 build、test、lint、codegen 这类命令是安全的。破坏性或贴近生产的命令别开,commit 前先看 diff。

build 只在 CI 挂,本地从不挂,为什么? 你几乎可以确定是原因 5 或 6:更严的 tsconfig.build.json、缺失的 env 变量 / secret、缺失的 fixture 文件,或 Node/pnpm 版本差异。本地用 tsc -p tsconfig.build.json --noEmit 加干净的 pnpm install 复现 CI,并检查每个新 env 变量在 .env.example 和 CI secrets 里都有。

Composer 把每条错都”修”了,但新错不断冒出来,怎么办? 这是在追级联错。别再逐行打补丁了。回滚到上一个绿的 commit,把改动作为一个整体重新 apply,让 Auto-run 驱动 pnpm build 跑到底,这样 agent 是一次性修根因,而不是治标。

相关阅读

标签: #排查 #Cursor #排查 #Build 挂