Overview
Use this skill to distill completed troubleshooting results into reusable long-term memory.
The goal is to preserve the root cause, effective fix path, incorrect assumptions, and verification evidence so that future agents do not repeat the same detours.
Core principle: record the decision chain, not a running log.
When to Use
Use this skill in the following scenarios:
- The user asks to update AI memory documents, record lessons learned, supplement incident records, or write a retrospective summary.
- The bug has been reproduced, and the effective fix path is clear.
- This lesson is repository-specific knowledge and should be visible to future agents.
- The conclusion needs to be synced to local MCP memory, such as Memorix.
Do not use this skill in the following cases:
- The bug is still under investigation, and the root cause has not been confirmed.
- What the user is asking for is a fix implementation, not experience distillation.
- You only have guesses, fragmentary evidence, or temporary workarounds.
Prerequisite Inputs
Before starting to write the memory, you must be able to answer the following six questions:
- From the user's perspective, what is the surface symptom?
- What is the actual root cause?
- Which incorrect assumption or misleading signal wasted time?
- Which specific change ultimately fixed the problem?
- What verification proves the fix holds?
- Where should this memory be written?
If any question cannot be answered, finish troubleshooting first, and do not write the memory prematurely.
Where to Write
- Repository-level, reusable rules: write to the root-level
CLAUDE.md、AGENTS.md、GEMINI.md - Cross-session local memory: write to Memorix, with type
gotcha、decisionorproblem-solution - Package-level prompts, plans, reports: write to them only when the user explicitly requests it
Default rule: as long as this lesson will affect future agents across the entire repository, prioritize writing it into the three root-level AI memory documents, and do not bury it in package-level notes.
What to Record
Each memory must cover at least these six things:
- Problem symptom: from the user's perspective, what is broken
- Root cause: where the real error is
- Key clue: which signal pulled the problem back from an illusion to the real root cause
- Effective fix: the change that truly solved the problem
- Verification method: evidence proving the fix succeeded
- Follow-up constraints: what future agents must check first and what to avoid
Memory Template
Use a concise structure oriented toward future reuse:
问题现象:...根因:...关键误导点:...有效修复:...验证方式:...后续约束:...
These sentences should help future agents quickly do the right thing, rather than retelling the complete troubleshooting process.
Repository-Level Knowledge Base
When the user asks to "supplement AI memory", do not just write the surface conclusion of the current bug. First check whether this problem falls into an existing incident pattern in the repository, then merge the corresponding lesson into the memory.
Reading hint: Each incident below uses a bolded dimension heading + a separate paragraph formatting for easy scanning; for similar issues, please add under the corresponding entrysupplement, to avoid opening a new paragraph repeatedly.
packages/vue-element-cui of vite@8 build incident
Issue symptoms
- During the build phase, a
vite:css-postofpathtype error is thrown, usually occurring during CSS asset artifact processing.
Actual root cause
rollupOptions.output.assetFileNamesdirectly returnsassetInfo.name, and when the CSS asset name is missing it returnsundefined。
Effective fix
- Do not downgrade dependencies, do not change dependency versions; prioritize adding a fallback value to the build configuration, for example
assetInfo.name ?? "assets/[name][extname]"。
Key takeaway
- When writing incident records in the future, it must be made clear that this is a "build configuration missing a fallback value" problem, rather than simply writing it as "vite version incompatibility".
Follow-up constraints
- When encountering the same type of problem again, first check the fallback return value of the build configuration, and do not immediately think of downgrading the toolchain.
packages/vue-element-cui-nuxt of nuxt dev startup incident
Issue symptoms
- The documentation site cannot be started directly in development mode, reporting missing
.nuxtartifacts, missing component library style artifacts, or the appearance of#app-manifest-related errors.
Actual root cause
- Incorrectly assuming that workspace dependencies have already been pre-built, and also incorrectly assuming that the
.nuxtdirectory exists in advance.
Effective fix
- Add
dev/buildtonuxt prepareas a prerequisite; configure source aliases for@eams-monorepo/vue-element-cuiand@eams-monorepo/vue-element-cui/styles; explicitly disable the currently unstableexperimental.appManifest。
Key takeaway
- When writing memory in the future, it must point out the constraint that "the documentation site development mode directly consumes source code", rather than only recording "just add a prepare and it will be fine".
Follow-up constraints
- Do not base the startup of the documentation site on the premise that the component library
distalready exists.
packages/vue-element-cui-nuxt documentation site client hydration incident
Issue symptoms
- Dark mode cannot be toggled, and clicking to collapse the sidebar has no effect. Do not prejudicially attribute this kind of interaction failure to styles.
Actual root cause
- Nuxt client hydration was interrupted by a dependency entry compatibility issue. The first trustworthy signal is the browser console error
dayjs.min.js does not provide an export named 'default', and subsequently it also cascades into@braintree/sanitize-url、debug、mermaidESM/CJS compatibility issues.
Effective fix
- Keep
extends: ["shadcn-docs-nuxt"]unchanged; prioritize making compatibility corrections at the Vite layer ofnuxt.config.ts, for exampledayjs -> dayjs/esm/index.js、mermaid -> mermaid/dist/mermaid.esm.mjs、debug -> ./shims/debug.ts, and supplementvite.optimizeDeps.include、vite.resolve.dedupe = ["dayjs"]、vite.ssr.noExternal = ["debug"];tailwind.config.jsonly as a content scanning fallback, and do not treat styles as the root cause.
Key takeaway
- When writing memory in the future, it must be made clear that "the first trustworthy clue comes from the Chrome MCP console, not from the visual symptoms themselves".
Follow-up constraints
- The troubleshooting order is fixed as "first console module errors, then dependency entries, then the style layer".
packages/vue-element-cui-nuxt stable startup baseline
Issue symptoms
- The project clearly used to run, but after subsequent modifications it was changed until it could no longer start.
Actual root cause
- The stable configuration was changed arbitrarily, especially continuing to make spreading modifications after the user had already confirmed that "it currently runs".
Effective fix
- Record the stable startup state confirmed by the user as the baseline, including the streamlined dependency set,
extends: ["shadcn-docs-nuxt"], monolingualzh-CNi18n、ogImage.enabled = false、icon.clientBundle.scan = true、vite.ssr.noExternaland other minimal viable combinations.
Key memory points
- When writing memories in the future, in addition to writing "how to fix it", you must also write "which configurations should not be touched casually".
Follow-up constraints
- Any subsequent troubleshooting and optimization must be premised on a stable baseline, to avoid breaking the project again.
packages/vue-element-cui-nuxt monolingual documentation and i18n misuse issues
Historical phenomenon
- The i18n logic in the template inheritance chain once caused the
/componentspage to show errors likeCannot read properties of undefined (reading children), and also brought redundant locale path handling.
Common root cause
- A monolingual documentation site should not unconditionally inherit the real multilingual runtime paths.
Key memory points
- When writing memories in the future, clearly distinguish whether the "compat override path" or the "explicit monolingual
defaultLocale/localesroute" was adopted at the time, and do not mix different historical fixes into one conclusion.
Follow-up constraints
- If the documentation site is essentially a monolingual site, it must be emphasized when recording that "do not treat multilingual runtime paths as the default premise".
simple-git-hooks hooks installed into a fake .git directory caused lint-staged to never trigger
Problem phenomenon
- Every time
git commit,lint-stagedandcommitlintwere not triggered, staged files were not formatted, and commit messages were not validated.
Actual root cause
- The Git repository root directory is at
01s-2603-13eams/(.gitat this level), while the pnpm monorepo workspace has in its subdirectoryeams-frontend-monorepo/。eams-frontend-monorepo/a fake.gitdirectory (containing only thehooks/subdirectory, with no HEAD, config, etc.),simple-git-hookswhenpostinstallsearches upward from the monorepo directory for.git, it first hit this fake directory, so the hooks were installed intoeams-frontend-monorepo/.git/hooks/. But when Git executes a commit, what it looks for is the real01s-2603-13eams/.git/hooks/(which only contains.samplefiles), so the hooks were never executed.
Key clues
git rev-parse --git-dirreturns01s-2603-13eams/.git, whilels eams-frontend-monorepo/.git/has only onehooks/subdirectory and no git metadata files, confirming that this is the fakesimple-git-hookscreated by.gititself. Comparing with the real.git/hooks/directory—it contains no non-.samplehook files, directly confirming that the hooks were installed in the wrong place.
Effective fix
- Three-step combined fix: (1) set
git config core.hooksPath eams-frontend-monorepo/.git/hooks, so that Git reads hooks from the monorepo's hooks directory; (2) updatesimple-git-hooks.mjs, add thecd eams-frontend-monorepoprefix to the hook commands, so thatnpxcan find dependencies and configuration under the monorepo directory; (3) thecommit-msghook usesROOT=$(pwd)to first save the absolute path of the repository root, and after cd uses"$ROOT/$1"to assemble the full path of the commit message file (because$1is a path relative to the repository root, and will become invalid after cd).
Verification method
git commit --allow-empty -m "test: hook trigger test"after seeing lint-staged output→ No staged files found., commitlint also did not reject valid messages, confirming that both hooks were triggered normally.
Follow-up constraints
- When the Git repository root and the monorepo workspace are not at the same level, you must check whether
git rev-parse --git-dirandsimple-git-hooksactually write hooks to the same location. Do not assume.gitdirectory andpackage.jsonare on the same level. Deleting the fake.gitis invalid—simple-git-hookswill be recreated, and it must be used together withcore.hooksPath.
packages/vue-element-cui-nuxt MDC documentation icon loss incident (prettier accidental formatting)
Problem symptoms
- On the documentation site
/getting-started、/components、/guidelines、/updatesall icons in the page cards disappeared, and props such astitle:、icon:、to:became plain text rendered directly on the page.
Actual root cause
prettierWhen formatting.mdfiles, it automatically inserts a blank line between the MDC component declaration line (such as::card) and the YAML frontmatter start marker (---). The MDC parser requires the two to be adjacent with no blank line; once there is a blank line,---is parsed as a<hr>tag, all YAML content becomes paragraph text, and all props become invalid.
Key misleading point
- At first it was thought that the icon bundle did not include the corresponding icon names, or that the
SmartIconcomponent was missing, and time was spent checking the icon library configuration. You should first look at the HTML source—there is no icon element in the HTML at all, which shows that the props were never parsed.
Effective fix
- (1) Create a new
.prettierignore, addpackages/vue-element-cui-nuxt/content/**/*.md; (2) in theprettier.config.mjsofoverrides, addrequirePragma: truefor that path as a double safeguard; (3) manually restore all::card\n---damaged by formatting (remove the blank lines in between).
Verification method
Invoke-WebRequest http://localhost:3001/getting-startedThe HTML returned byi-lucide:download、i-lucide:rocket、i-lucide:life-buoy、i-lucide:arrow-left-rightcontains the four icon classes.
Follow-up constraints
- MDC content files must never be formatted by prettier;
.prettierignoremust cover all nuxt content directories; when troubleshooting disappearing icons, first check whether an icon element exists in the HTML source, rather than checking the icon library configuration first.
packages/vue-element-cui-nuxt of ::demo-playground rendering failure incident (MDC container syntax error, 2026-03)
Problem symptoms
- Multiple component documentation pages showed raw text such as
::demo-playground、title:、#preview、#coderendered directly; the browser console showedHydration completed but contains mismatchesand a large number of hydration node mismatch warnings.
Actual root cause
demo-playgroundThe container was not written according to MDC syntax. Typical errors include: (1) writing it as## ::demo-playground(treated as a heading); (2) incorrect frontmatter order (title/descriptionfirst,---after); (3) mismatched container opening and closing markers.
Key misleading point
- It is easy to first suspect the
DemoPlaygroundcomponent implementation or Nuxt dependency compatibility issues. In fact, the first high-value clue is that “raw marker text appears in the body + hydration mismatch appears at the same time,” and you should first go back and check the markdown container syntax.
Effective fix
- Unify all example blocks as
::demo-playground+ immediately followed by---frontmatter +::closing, and keep#previewand#codeinside the container; after batch modification, sample and regress multiple routes.
Verification method
- In Chrome MCP, passing requires three points: (1) no hydration mismatch errors in the console; (2) the page body no longer shows
::demo-playground/#preview/#code/title:raw text; (3) the demo title and description (from frontmatter) render normally.
Follow-up constraints
- When adding new demo documentation later, it is forbidden to use
## ::demo-playground; do not reorder the frontmatter; batch replacement scripts may only modify the target container fragment, and you must sample first before doing a full run to avoid accidentally changing other:::containers.
packages/vue-element-cui-nuxt Nuxt SSR i18n version conflict incident (pnpm dependency hoisting)
Symptoms
nuxt devAfter startup, all pages return a 500 error, with the error message beingError: (0, __vite_ssr_import_0__.registerMessageResolver) is not a function。
Actual root cause
@intlify/core-baseMultiple versions exist (9.1.9 and 11.x) simultaneously in the dependency tree.shamefully-hoist=falsecauses pnpm to manage dependencies in isolated mode, and during Vite SSR resolution it hit the old version 9.1.9 (missingregisterMessageResolver), rather than the 11.x version required byvue-i18n@11.x. After upgradingshamefully-hoist=true, the old versionsass@1.26.8was hoisted instead, causingsass.initAsyncCompiler is not a function。
Key misleading points
- Multiple attempts were made with
vite.ssr.noExternal、vite.resolve.alias、vite.resolve.dedupeand other nuxt.config.ts configurations, all of which were ineffective directions; the correct direction is to force pnpm to use a single version across the entire workspace, rather than bypassing it at the Vite layer.
Effective fix
- (1) In
.npmrc, change it toshamefully-hoist=true; (2) Inpnpm-workspace.yaml'soverrides, add"@intlify/core-base": "11.3.0"、"@intlify/shared": "11.3.0"、"sass": "^1.98.0"; (3) In the rootpackage.json, upgradevue-i18nto"11.3.0"; (4) Runpnpm installin a terminal outside Cursor to make the overrides take effect.
Verification method
- The Nuxt dev server starts without a 500 error, and all pages return 200 OK; in
node_modules/@intlify/core-base/package.json, theversionfield is11.3.0。
Follow-up constraints
- When encountering a Nuxt SSR
is not a functionerror, first usepnpm why <package>to check whether multiple version instances exist, then force a single version throughpnpm-workspace.yaml'soverrides; do not make changingnuxt.config.ts's Vite-layer configuration your first reaction.
Cursor IDE holding .node native file lock causing pnpm install to fail
Symptoms
- When running
pnpm installin Cursor's built-in terminal, it reportsEPERM: operation not permitted, unlink 'node_modules/@oxc-parser/binding-win32-x64-msvc/parser.win32-x64-msvc.node', the installation rolls back, and dependency changes cannot take effect.
Actual root cause
- Cursor IDE's TypeScript language service process (tsserver) holds a Windows file lock on the
@oxc-parsernative.nodefile, and pnpm cannot delete that file to complete the dependency update, causing the entire installation transaction to roll back.
Effective fix
- Run
pnpm installin a terminal outside Cursor (such as PowerShell or system CMD) to bypass the IDE file lock.
Verification method
- After
pnpm installfinishes in the external terminal, there is no EPERM error, andpnpm-lock.yamlis updated correctly.
Follow-up constraints
- When updating any dependency involving a native Node.js addon in this monorepo (such as
@oxc-parser、esbuild、@swc/*), you must runpnpm installin a terminal outside Cursor; Cursor's built-in terminal is only for reading logs, not for executing install.
Repository root .gitattributes and eol=lf causing CRLF/LF "ghost" differences (2026-03)
Symptoms
- For example,
eams-frontend-monorepo/README.mdon multiplef1-*sub-branchesalways shows as modified (M), and it does not disappear when switching branches, blocking merges;git diffOften only the whole-file "line ending change" is shown with no substantive content difference.
Actual root cause
- Root directory
.gitattributesalready specifies text fileseol=lf, and Git writes the working tree as LF on checkout, butthe historical blob of that file in each branch's index is still CRLF; the line-ending policy of the index and the working tree are inconsistent, creating a persistent dirty state.
Key misleading points
- Mistaken for "a single branch not saved" or "the IDE messing things up"; changing the file on only one branch is not enough,every branch whose index still contains CRLF must be
git add --renormalizeand then committed, otherwise it reappears as soon as you switch back.
Effective fix
- Run
git add --renormalize <path>on the path (or the whole repository as needed,git add --renormalize .) and commit, so that the object database is consistent with.gitattributes; in the monorepoeams-frontend-monorepo/.editorconfig's[*], addend_of_line = lfto reduce new files defaulting to CRLF on Windows. When gathering changes from each sub-branch intof1, the merge commit message must pass commitlint, using forms such aschore: merge <branch> into f1, and avoid using illegal types (such asmerge:) that cause the merge not to complete.
Verification method
- On multiple
f1-*branches, aftergit checkout,git statushas no such file;git diffis empty for that path.
Follow-up constraints
- After introducing or tightening
eol=lf, if "text files that are forever M" appear, first checkgit diffto see whether it is only CRLF↔LF andgit add --renormalize, and do not first make large-scale changes to business code; before batch-merging sub-branches into the main branch, confirm that the commit messages comply with this repository's Conventional Commits rules.
packages/vue-element-cui-nuxt dev warning cleanup experience
Historical phenomenon
- Even if the page can open,
nuxt devmay still contain warnings for i18n, OG Image, Icon, Sass, etc.
Actual root cause
- This is usually not a single-point problem, but the accumulation of multiple small compatibility issues, such as missing
defaultLocale, same-named composables overlapping with auto-imports, callingdefineOgImageComponent()even though it is not enabled, missing locallucidecollections, styles still using globalmix()。
Effective fix
- Explicitly complete the monolingual i18n configuration; avoid duplicate auto-imports of same-named helpers;
ogImage.enabled = falsewhen doing so, remove thedefineOgImageComponent()call via page overrides instead of forcibly enabling the module; install@iconify-json/lucide; migrate Sass'smix()tocolor.mix()。
Key memory points
- When writing memory in the future, explain that warning cleanup must be based on new logs from a "single fresh dev process", not on guesses pieced together from old logs.
Follow-up constraints
- When verifying conclusions, prioritize recording whether
fresh dev.stderris empty, whether the page HTTP is 200, and whether the Chrome console has no newwarn/error。
packages/vue-element-cui-nuxt Windows PowerShell build false hang and Nitro trace incident (2026-03)
Problem symptoms
pnpm --filter @eams-monorepo/vue-element-cui-nuxt buildstays atBuilding Nuxt Nitro server (preset: node-server)for a long time; after multiple timeout reruns, it looks like "the more it runs, the more stuck it gets, until it is completely stuck".
Actual root cause
- There are two layers of causes combined. The first layer is the real blocking point: the Nitro node-server packaging stage defaults to
externals.tracewhich triggersnodeFileTrace, and in the current docs site + pnpm workspace + Windows environment, it continuously consumes high CPU and high memory, and the build gets stuck for a long time at the Nitro finishing stage. The second layer is the illusion magnified: under PowerShell, timeout termination, manual interruption, orStart-Processrunning in the backgroundpnpm -> cmd -> node -> nuxt buildwhen the outer command ends, it does not mean the inner child process tree has exited; the old build chain will remain and overlap with the new build.
Key misleading point
- The log stopping on the same line does not mean the process is already idling; if you do not first clean up the leftover child processes, subsequent
Get-Process, log, and artifact observations will mistake old process noise for the state of the current command.
Key clue
- When reproducing with a single process,
.nuxt/dist/server/server.mjshas already been generated, but.output/serveris still empty, indicating that Vite SSR has completed and the stuck point is at the Nitro finishing stage; at the same time,nuxt.mjs build's working set can rise above 2GB, and CPU still keeps increasing, which matches the characteristics ofnitropack'snodeFileTracetracing stage.
Effective fix
- First clean up the old
pnpm -> cmd -> nodebuild chain according to command-line characteristics, keeping only one isolated process for reproduction; then explicitly setpackages/vue-element-cui-nuxt/nuxt.config.tsinnitro.externals.inline = [/.*/](rather thantrace = false), so that Rollup inlines all dependencies into the server bundle, while bypassing the@vercel/nfttracing deadlock issue under Windows + pnpm workspace.
Verification method
- Run
pnpm --filter @eams-monorepo/vue-element-cui-nuxt exec nuxi build --logLevel=verboseas a single process; it should generate.output/server/index.mjsand printBuild complete!; at the same time, confirm that the target build process chain is no longer left behind.
Follow-up constraints
- When reproducing "build deadlock" under Windows PowerShell in the future, first confirm whether old child processes remain, then determine whether the current command is actually stuck; do not directly attribute the phenomenon after multiple leftover build chains overlap to the current change.
- Do not use
nitro.externals.trace = false—although it can bypass the tracing deadlock, it will cause the Vercel cloud function artifact to lacknode_modulesandpackage.json, and error directly at runtime (see "Vercel Cloud Function Missing Dependency Incident" below). The correct workaround isexternals.inline = [/.*/], so that dependencies are inlined into the bundle rather than relying on externalnode_modules。
packages/vue-element-cui-nuxt 's Vercel cloud function entities/decode crash incident (multi-version dependencies + Nitro externals chain of pitfalls, 2026-04)
Problem symptoms
- After the docs site is deployed to Vercel, all requests return
500 FUNCTION_INVOCATION_FAILED, and the runtime logs uniformly reportCannot find module 'entities/decode', with the require stack pointing to/var/task/node_modules/@vue/compiler-core/dist/compiler-core.cjs.prod.js。
Actual root cause (two layers stacked)
- First layer (initial trigger):
nuxt.config.tsinnitro.externals.trace = falsedisabled@vercel/nft(Node File Trace), causing the build artifact__fallback.func/to completely contain nonode_modulesandpackage.json, and the cloud function cannot find any external dependencies at runtime. - Second layer (the real root cause)There are three different versions of
entities(4.5.0via svgo、6.0.1via parse5、7.0.1via@vue/compiler-core@3.5.30)。@vue/compiler-core's CJS production build performingrequire('entities/decode')at runtime, requiringentities@7.x's./decodesubpath export. When Nitro's@vercel/nfttraces dependencies, it may trace the wrong version (4.xor6.x), or when Vercel installs dependencies from the function'spackage.json, it may pull the wrong version, causingentities/decodeunavailable at runtime.
Key misleading points and pitfalls
- Misleading point 1: Initially thought that simply removing
trace: falsewould fix it. But even after removal, when Vercel builds on Linux, Nitro's default trace still did not correctly includeentities@7.x(because multiple versions coexist, the trace may select the wrong version). - Misleading point 2: Switching to
externals.inline = [/.*/]could generate a self-contained bundle when building locally on Windows ("dependencies": {}), and it looked fixed. But the Vercel build framework, after Nitro, wouldrun its own@vercel/nftagain, and after detecting residualrequire()references in the bundle, recopied an incomplete Vue runtime from the projectnode_modules(missing the correct version ofentities), causing the same runtime error. - Misleading point 3: Assuming that "correct local build artifacts = correct Vercel deployment." In reality, Vercel rebuilds on its own Linux server, and the pnpm workspace's
node_modulesstructure is different, turbo cache behavior is different, and@vercel/nfttracing results are also different.Local acceptance cannot replace actual Vercel deployment verification.
Effective fix (two-step combination)
- (1) Remove all
nuxt.config.tsinnitro.externalsconfiguration, letting Nitro use the default@vercel/nfttrace behavior (Vercel builds on Linux, so the Windows trace hang issue does not exist). - (2) Add
entitiesas an explicitpackages/vue-element-cui-nuxt/package.jsonofdependencies("entities": "^7.0.1"), and inpnpm-workspace.yamlofoverridesaddentities: "^7.0.1", forcing the entire workspace to use a unified7.xversion and eliminating multi-version ambiguity.
Verification method
- After Vercel deploys commit
211c662,https://vec.ruan-cat.com/no longer returnsFUNCTION_INVOCATION_FAILED, but instead returns a Nuxt application-layer JSON error response ({"statusCode": 500, "statusMessage": "Server Error"}) - proving that the cloud function has started normally and theentities/decodemodule is available; the Nuxt Content API (such as/api/_content/query) correctly responds with 404 (content not found) instead of crashing due to a missing module.
Subsequent constraints
- Never use
nitro.externals.trace = false. It causes cloud functions on serverless platforms such as Vercel/Netlify to fail to find dependencies at runtime. - Do not use
nitro.externals.inline = [/.*/]to fix Vercel deployments. It works locally, but the Vercel build framework will run@vercel/nftagain after Nitro, reintroducing an incompletenode_modules。 - When a dependency tree has multiple versions, you must use
pnpm-workspace.yamltheoverridesto unify and lock it, especially for packages depended on by runtime CJSrequire()subpath exports (such asentities)。 - Vercel deployment issues cannot be verified only locally. Local builds and Vercel builds may differ in trace results,
node_modulesstructure, and cache behavior, so you must confirm the runtime state through Vercel MCP or by directly accessing the deployment URL. - When troubleshooting Vercel deployment failures, the first checkpoint iserror type:
FUNCTION_INVOCATION_FAILED(module missing/function crash) vs Nuxt JSON error response (application-layer SSR error); the troubleshooting paths for the two are completely different.
Vercel cloud function @vueuse/core Module missing incident — workspace package + Vite SSR externalization chain of pitfalls (2026-04)
Symptom
- After the docs site was deployed to Vercel, at runtime it threw
ERR_MODULE_NOT_FOUND: Cannot find package '@vueuse/core' imported from /var/task/node_modules/element-plus/es/hooks/use-calc-input-width/index.mjs, and the cloud function crashed and returned 500.
Actual root causes (three layers stacked)
- First layer (Vite SSR externalization): by default, the Vite SSR build marks packages in
node_modulesas external, including workspace packages@eams-monorepo/vue-element-cuiandelement-plus. This means that at runtime they need to be loaded fromnode_modules. - Second layer (workspace package import chain):
@eams-monorepo/vue-element-cui(introduced viaworkspace:*) compiled outputdist/index.jscontainsimport ... from 'element-plus',element-plusESM files that in turnimport ... from '@vueuse/core'. Complete chain: Nuxt SSR → workspace package (external) → element-plus (external) → @vueuse/core (missing). - Third layer (pnpm multiple versions + NFT trace failure):
@vueuse/corehas 3 versions in the workspace (v12/v13/v14), and pnpm's symlink structure causes@vercel/nftto fail to trace the correct version, so@vueuse/coreis not included in the cloud function'snode_modules.
Key misleading points and the path of pitfalls
- Misleading point 1: assuming
nitro.externals.inlinecan solve the problem. In fact, Nitro's inline acts at the Nitro Rollup stage, but Vite SSR has already externalized these packages at an earlier stage; what Nitro receives is already-externalized import statements, and the inline configuration has no chance to intervene. - Misleading point 2: assuming that simply adding
@vueuse/coreas a direct dependency would be enough. But there are 3 major versions (v12/v13/v14), and overriding to a single version may break compatibility (element-plus needs v12, shadcn-docs-nuxt needs v14). - Misleading point 3: ignoring the special nature of workspace packages in SSR. Workspace packages exist in
node_modulesvia symlinks, and Vite externalizes them by default. Their internal imports ofelement-plusare resolved at runtime from the workspace package's directory context, not from the Nuxt app's directory.
Effective fix
- In
nuxt.config.ts'svite.ssr.noExternal, list the workspace packages andelement-plus's complete runtime dependency tree to prevent Vite SSR from externalizing them and force them into the server bundle at build time:vite: { ssr: { noExternal: [ "@eams-monorepo/vue-element-cui", // workspace 包 /element-plus/, /@element-plus/, /@vueuse/, /vue-demi/, /@ctrl\/tinycolor/, /@floating-ui/, /@popperjs\/core/, /async-validator/, /escape-html/, /lodash-unified/, /lodash-es/, /memoize-one/, /normalize-wheel-es/, /entities/, ], }, } - At the same time, combine with
isWindows(fromstd-env) to conditionally skip NFT trace during local builds on Windows (to avoid hanging); Vercel Linux builds trace normally.
Verification method
- Local build: confirm that
element-pluscode appears in__fallback.func/chunks/'s server chunks (rather thannode_modules), and the bundle size grows from 5.87 MB to 8.38 MB. - Vercel deployment:
https://vec.ruan-cat.com/returns HTTP 200 (rather than the previous 500 crash),/api/_content/navigationresponds normally (no longer reportingERR_MODULE_NOT_FOUND)。
Subsequent constraints
- When solving Vercel SSR missing-dependency issues, check
vite.ssr.noExternalfirst, rather thannitro.externals.inline. The two act at different stages (Vite SSR build vs Nitro Rollup), and the former is the fundamental fix point. - Workspace packages (
workspace:*) must be added tonoExternalin SSR scenarios; otherwise loading via symlinks at runtime will lose the dependency context. - When adding
noExternalentries, you must list the target package'scomplete runtime dependency tree(such aselement-plus→@vueuse/core→@vueuse/shared); omitting any single transitive dependency may cause newERR_MODULE_NOT_FOUND。 - When using
std-envand other non-Node.js built-in packages, you must explicitly declare them in the current subpackage'spackage.jsonto avoid phantom dependencies.
Complete production failure of the Nuxt Content documentation site—a Windows local build patch triggered a cascading collapse of Vercel Linux deployment (2026-04 full postmortem)
Scope:packages/vue-element-cui-nuxt(component library documentation site)
Time:2026-04
Keywords: pnpm monorepo, Vercel serverless, Nitro, Vite SSR, @vercel/nft, prerendering, document-driven, workspace packages, multi-version dependencies
Full Picture of the Incident
This is acascading collapse triggered by a Windows local build optimization and exposed in the Vercel Linux production environment. The @vercel/nft added initially to address Windows local nitro.externals.trace = false trace hangs and the prerender:routes clearing hook continued to affect Vercel deployments over the following months, ultimately leading to a three-layer compounded failure:
| Layer | Symptom | Root Cause |
|---|---|---|
| First layer | FUNCTION_INVOCATION_FAILED,Cannot find module 'entities/decode' |
trace: false caused the cloud function to be missing node_modules; subsequent entities multi-version ambiguity |
| Second layer | ERR_MODULE_NOT_FOUND: Cannot find package '@vueuse/core' |
element-plus was externalized by Vite SSR; the workspace package import chain + pnpm symlinks caused NFT tracing to fail |
| Third layer | TypeError: Cannot read properties of null (reading '_id') |
prerender:routes clearing hook disabled prerendering; document-driven Nuxt Content could not parse markdown into structured objects at build time, and the runtime content database was empty |
Core Lessons
Do not use a patch that "works locally" to solve production environment problems. Windows and Linux have completely different node_modules structures,@vercel/nft behavior, and prerendering processes. Every workaround added for Windows may create new problems on Linux:
| Windows Patch | Local Problem Solved | Production Problem Caused |
|---|---|---|
nitro.externals.trace = false |
NFT trace hangs | Cloud function completely missing node_modules |
nitro.externals.inline = [/.*/] |
Bypass trace hangs | Vercel's secondary NFT tracing introduced incomplete node_modules |
prerender: { crawlLinks: false } + routes.clear() |
Reduce build time | Content documents were not pre-parsed, and the runtime database was empty |
Final Fix Plan (all used together)
vite.ssr.noExternal: Add the workspace package@eams-monorepo/vue-element-cuiandelement-pluscomplete dependency tree tonoExternal, preventing Vite SSR externalization and bundling them into the server bundle at build time.nitro.externals.inline: Keep a precise inline list as a supplement (mutually insuring withssr.noExternal).isWindows ? { trace: false } : {}: Usestd-envto detect the platform; skip NFT trace only for Windows local, and perform normal trace on Vercel Linux.prerender: { crawlLinks: true }: Restore prerendering so that document-driven Nuxt Content parses markdown into structured objects at build time.pnpm-workspace.yamloverrides: Lockentities: "^7.0.1"to eliminate multi-version ambiguity.- Explicit Dependency Declaration:
entities、std-envand other packages are explicitly added to the subpackagepackage.json, eliminating phantom dependencies.
Subsequent Constraints
- Always verify with an actual deployment on Vercel first, and do not use local build results as the final criterion.
- Unconditionally using
trace: falseorinline: [/.*/]is prohibited; it must be made conditional based on platform detection. - Do not clear prerendered routes(
routes.clear()); document-driven Nuxt Content depends on parsing content during the prerender phase. - Workspace packages must be added in SSR scenarios to
vite.ssr.noExternal。 - When fixing Windows build issues, the impact on the Linux production environment must also be evaluated—the pnpm symlink structure, NFT tracing behavior, and prerendering process all differ between the two platforms.
PowerShell Git Batch Merge Script Incident
Scope:eams-frontend-monorepo/scripts
Time:2026-03
Keywords: UTF-8 BOM, pipeline misuse, git hooks,stderr、$LASTEXITCODE、--no-ff
Symptoms
False syntax errors
- A
.ps1containing Chinese reports under Windows PowerShell 5 errors such as "The string is missing the terminator" and "Unexpected token}", etc.; this is actually an encoding parsing issue, not a logic error.
False "conflict" reports and merge --abort failures
git merge origin/f1actually succeeded, but the script enteredcatchand reported "conflict or failure".- The message is mixed with hook output, such as
ℹ 输出 N 个暂存区文件路径, ANSI color codes. - Subsequently executing
git merge --abortreportsfatal: There is no merge to abort, becauseMERGE_HEADno longer exists.
Misunderstanding of push cadence
- Mistakenly believed that "the merge succeeded but was not pushed to the remote"; this needs to be aligned with the product: a single
pushat each steppush。
Abnormal bloat of the history graph
- The batch merge script used
git merge --no-fffor a long time; even when fast-forwarding was possible, it forcibly added merge nodes. - The result was that the branch graph showed a large number of extra merge commits, making it harder to read and trace, and obscuring the real conflict points.
Actual Root Causes
Encoding and PS5
- UTF-8 without BOM is read by PS5 according to the system code page, and the Chinese bytes break the closing quotation marks.
$ErrorActionPreference, pipelines and hooks stderr
- At the top of the script
$ErrorActionPreference = "Stop"andgit merge ... 2>&1 | ForEach-Object { ... }are used together. - After the merge completes, post-merge and other hooks may still write information to stderr .
- After the pipeline merges
stderrin, PowerShell easily treats the nativestderras aterminating error, and directly enterscatch; at this point the merge has already ended,MERGE_HEADhas been cleared, andmerge --aborthas no merge to abort.
Pipelines and exit codes
$LASTEXITCODEafter a pipeline cannot reliably representgit mergeitself, for the same reason asgit push.
Improper merge strategy configuration
- Enabled by default in batch synchronization scenarios,
--no-ff, which is strategy abuse. - This configuration turns "preserve explicit merge nodes" from an exception into the norm, resulting in excessively noisy commit graphs.
Key Misleading Points
- Treating the text in
catchas a real merge conflict. - Treating the fatal from
merge --abortas repository corruption. - First check whether
MERGE_HEADexists, whether a merge commit has already been produced; if the merge has already succeeded, it is mostly a false positive.
Effective fix
Script encoding
- Save as UTF-8 with BOM; if the BOM is lost after saving in the editor, it needs to be written again.
How git is invoked
- Do notwrap
git merge/git push。 - Execute
git ...directly, then useif ($LASTEXITCODE -ne 0)to determine success or failure.
Remote update cadence
- If remote every step visibility is required: in
merge-all-branches-to-f1.ps1ineach timeafter a successful merge, executegit push origin f1。 - and
merge-f1-to-all-branches.ps1align with the strategy of "push immediately after each sub-branch merge".
Merge strategy reverts to default
- Remove
--no-fffrom the script, restoregit merge --no-editdefault behavior. - Let Git fast-forward directly when possible, and only produce a merge node when there is an actual divergence.
Verification method
Parser::ParseFilehas no syntax errors for.ps1.- Actually run it through: successfully merged branches can normally
git push。 - "merge succeeded but abort is executed" no longer occurs.
- On the sub-branch,
git statushas no unexpected conflict state.
Subsequent constraints
Encoding and git invocation
- This repository's PowerShell + git automation defaults to UTF-8 BOM。
- forbiddento use
git merge/git pushwith2>&1 |pipe; consistent with the "push without pipe" comment in the script. - When troubleshooting, if you see hook-related
stderrtext, first suspect Stop + pipe, rather than a real merge failure. - The batch sync script uses normal merge behavior by default,abuse of
--no-ffis forbidden; if the business explicitly requires "never add a merge node", it should be changed to--ff-onlyand accept abort on failure.
Where the experience lands
- When adding similar explanationsdo notin
.cursor/skillscreate a separate parallel SKILL; instead merge the incident patterninto this skill's repository-level experience library.
packages/vue-element-cui CuiDialog overlay completely failing incident (Element Plus appendToBody default value change + CSS stacking context, 2026-04)
Problem symptoms
- On the documentation site (reproduced in both dev and production environments), after clicking the "Open confirmation overlay" button, the dialog pops up butthere is no semi-transparent overlay covering the page—the header, sidebar, and body content behind it are all clearly visible and clickable; the dialog merely floats above the page, and the visual hierarchy is completely disordered.
Actual root cause (two layers stacked)
- First layer (Teleport disabled): Element Plus 2.13.x changed the dialog's
appendToBodyproperty type toBoolean(no explicit default value); in Vue 3, when a Boolean prop is not passed it defaults tofalse. In the source code,ElTeleport'sdisabledevaluates to!appendToBody, i.e.!false = true, causing<Teleport to="body">to be disabled. The overlay is no longer mounted to<body>, but remains in the DOM level where the component is located. - Second layer (CSS stacking context traps the overlay): the documentation site's demo container
.cui-demo-containeris set withisolation: isolate(for style isolation), creating a new stacking context. Although the overlay isposition: fixedandz-index: 2002, its z-index only takes effect within.cui-demo-container's stacking context, and cannot cover elements such as the header (z-index: 40)、sidebar(z-index: 30) in the upper layers of the page.
Key misleading points
- Misleading point 1: At first, it was suspected that
nuxt.config.tsinvite.ssr.noExternal's overly aggressive dependency externalization configuration caused the CSS to be lost. After actually checking the production CSS file (367KB), it was confirmed that all Element Plus style classes (.el-dialog、.el-overlay、.el-button), CSS variables (--el-color-primary、--el-overlay-color-lighter) exist and are complete. - Misleading point 2: It was suspected that Tailwind CSS v4's
@layer basepreflight reset overrode Element Plus styles. In fact, Element Plus CSS is unlayered styles, which always take precedence over rules in@layer basein the CSS cascade. - Misleading point 3: When inspecting the overlay via
getComputedStyle, all properties look completely correct (background-color: rgba(0,0,0,0.5)、position: fixed、inset: 0px、z-index: 2002), but visually there is no mask effect on the page.Key turning pointwas checkingoverlay.parentElementand finding that its parent node isDIV.space-y-4(inside the demo component) rather thanBODY, then tracing the ancestor chain to findisolation: isolate。
Effective fix
- In the
CuiDialogcomponent's<el-dialog>, add theappend-to-bodyattribute to force-enable Teleport to mount the overlay to the<body>level, bypassing.cui-demo-container's stacking context limitation.
Verification method
document.querySelector('.el-overlay').parentElement === document.bodyreturnstrue, confirming that the overlay has been mounted to body.document.elementFromPoint()At various positions on the page (header, sidebar, content, corner), all returnel-overlay-dialog, confirming that the mask covers the entire page.- All 292 component library tests pass, with no regressions.
Subsequent constraints
- After upgrading Element Plus, the Teleport behavior of popup components must be checked. Element Plus's
appendToBody/teleporteddefault values change between major versions (old versionteleporteddefaults totrue, new versionappendToBodydefaults tofalse). After upgrading, if the popup does not show a mask, check this item first. - When using
isolation: isolateto create a stacking context, you must also ensure that the internal popup component has Teleport enabled, otherwise the overlay'sz-indexwill be trapped within that context. - When troubleshooting "overlay styles are correct but visually ineffective", the first checkpoint is whether
overlay.parentElementisbody, not checking CSS property values. The most common reason an overlay is invisible even though all CSS properties are correct is failed Teleport + stacking context.
Additional information that must be retained when writing the experience
If this bug is similar to an existing incident pattern in the repository, do not omit the following additional information when writing the memory:
- Whether this issue broke a baseline that a "user has confirmed as stable"
- Whether there is a "do not change arbitrarily" configuration, for example
extends: ["shadcn-docs-nuxt"] - Where does the first trustworthy signal come from: terminal logs, browser console, network requests, or build output
- Which category does this fix belong to: build configuration fallback, dependency entry compatibility, template-layer override, style-layer completion, or startup pre-preparation
- Is there a highly misleading illusion this time, for example "looks like a style issue, but is actually hydration"
- Is the final verification based on a fresh process, fresh logs, and fresh page rather than historical cache
How to write verification evidence
When writing incident records in the future, prioritize recording evidence that can be repeatedly verified, rather than vague wording.
Recommended evidence wording
fresh dev.stderr 为空/componentsReturns 200Chrome consoleNo new warn/error- Dark mode toggle restored, sidebar collapse restored
Not recommended vague wording
- "It should be fine now"
- "It looks like it's fixed"
What not to write
Absorbing root-level AI memory experience into the skill does not mean writing the skill as a repair manual. The following content should not become the main body of this skill:
- Long command execution logs
- Generalized debug theory unrelated to the current repository
- Listing every trial-and-error process one by one
- Packaging a one-time temporary workaround as a permanent rule
- Using "these commands must be executed" instead of "which conclusions should be recorded"
Recording process
- First confirm that the bug is clearly understood and the fix is complete.
- Compress the result into 4 to 6 high-signal facts.
- Choose the right memory destination.
- If it is repository-level experience, update the root-level AI memory document.
- Update Memorix with the same conclusions, and choose the right memory type.
- Read the text back once and delete transient noise, guesses, and low-value command history.
- If the user also asks to commit, hand off the commit action to a separate git workflow.
Characteristics of good memory
- Explain clearly "why it broke", rather than only writing what commands were run
- Clearly identify the first trustworthy clue and explain how it broke the wrong assumption
- Describe the final fix in a reusable way
- Write verification actions that future agents can repeat
- Make the next troubleshooting session noticeably shorter
Common mistakes
- Starting to write speculative conclusions before the root cause is confirmed
- Writing a very long debug diary instead of reusable conclusions
- Writing repository-level experience in the wrong place
- Failing to write out the wrong assumptions that caused the detour
- Mixing fix instructions and memory distillation together
- Forgetting to sync local MCP memory
Boundary
This skill is only responsible for memory distillation and summarization.
It cannot replace debugging, implementation, testing, and repair workflows. If the bug is not fixed yet, first use the appropriate debugging or implementation skill, and return to this skill for experience distillation after the result is stable.
Additional error-prone point: integration changes must not delete or reduce original comments and component description information
Problem symptoms
- To integrate a new tool or adjust the initialization order, directly rewriting the entry file or component file in full, causing the original comments, explanatory text, naming semantics, and information conveyed by the component to be erased along with it.
Actual root cause
- Mistaking "feature integration" for "permission to overwrite the original file's expression layer," focusing only on whether the code still runs, and not treating comments, explanatory copy, and structural naming as part of the existing implementation.
Key misleading points
- On the surface, it is merely restoring the import order, plugin order, or template structure, so it is easy to mistakenly assume that "if the logic is equivalent, there is no problem"; but for this repository, the original comments and component descriptions are themselves conventions and context, not noise that can be discarded at will.
Effective fix
- Prioritize minimal patch-style changes, preserving the original comments, original explanatory text, and original component expression; if an entire section must be rewritten, first reread the baseline file, migrate the original comments and explanatory information back together, and then add the new integration notes.
Verification method
- Compare the target file before and after modification to confirm that the original comment blocks, original explanatory text, and original component semantics are still present; do not verify only that it runs, but also verify that the information expression has not been reduced.
Follow-up constraints
- Subsequently, in
main.ts, sample pages, component READMEs, and component entry files, when integrating tools or adjusting structure, do not use overwrite-style rewriting instead of local modification; do not delete comments, and do not erode the information originally conveyed by the component.
Misuse of phantom dependencies in a pnpm monorepo (2026-04)
Problem symptoms
- In
packages/vue-element-cui-nuxt/nuxt.config.tsdirectlyimport { isWindows } from "std-env", butpackages/vue-element-cui-nuxt/package.jsondid not declarestd-envas a dependency. Locally, because.npmrc'sshamefully-hoist=truehoistedstd-env(a transitive dependency of Nuxt) to the rootnode_modules, the code could resolve normally, but this is a typical phantom dependency.
Actual root cause
- When AI replaced
import { platform } from "node:os"withstd-env, it only confirmed that "the Nuxt ecosystem already hasstd-envbuilt in" and used it directly, without checking whether the currentpackage.jsonexplicitly declared that dependency. Under pnpm strict mode or other package managers, this kind of phantom dependency will directly causeERR_MODULE_NOT_FOUND。
Key misleading points
- "Nuxt/Nitro already has the dependency built in" easily leads people to think they can import it directly, ignoring pnpm's dependency isolation mechanism.
shamefully-hoist=truemasks the phantom dependency problem, local development works fine, but deployment or other environments may fail.
Effective fix
- In
packages/vue-element-cui-nuxt/package.json'sdependencies, explicitly add"std-env": "^3.10.0"。
Verification method
pnpm why std-envconfirm that the dependency chain is correct; checkpackage.jsonto confirm that the explicit declaration exists.
Follow-up constraints
- When importing third-party packages in any subpackage of a pnpm monorepo,you must first confirm that the package has already been declared explicitly in the current subpackage's
package.jsonexplicitly declared in, regardless of whether it is installed indirectly by an upper-level dependency. Even for known internal dependencies of frameworks such as Nuxt, Nitro, and Vite (such asstd-env、pathe、defu、consola, etc.), you must not skip the explicit declaration and use them directly. - If the functionality can be implemented with Node.js built-in modules (such as
node:os、node:path), prioritize using the built-in modules to avoid introducing unnecessary external dependencies.