`npm install -g` fails with `EBUSY … unlink '.nfsXXXX'` (qmd upgrade)
Source: Troubleshooting reference
bootstrap.sh upgrade (or install/node.sh) dies upgrading a global npm
package — almost always @tobilu/qmd:
npm error code EBUSYnpm error EBUSY: resource busy or locked, unlink'.../@tobilu/qmd/node_modules/sqlite-vec-linux-x64/.nfs000000001f79d0f000015a88'[fail] node.sh failedRoot cause: NFS “silly-rename”. The qmd MCP daemon
(qmd mcp --http --port 8181) keeps native addons (sqlite-vec,
node-llama-cpp, better-sqlite3) mmap’d. When npm deletes the old package
tree to swap in the new one, NFS can’t remove a file the daemon still has open,
so it renames it to .nfsXXXX and keeps it until that fd closes. npm then can’t
unlink the .nfs* file and aborts with EBUSY. Only happens on NFS homes
(the Linux clusters) — macOS local disks unlink open files fine, so this is
gated to Linux.
node.sh now stops the daemon around the qmd upgrade and restarts it (via the
qmd_daemon_* helpers in _lib.sh), so a normal upgrade no longer trips on it.
To recover a checkout that predates the fix, or if you hit it by hand:
pkill -f "qmd[^ ]* mcp --http" # 1. stop the daemon → NFS reaps .nfs* filesnpm install -g @tobilu/qmd@latest # 2. re-run the upgrade (or: bash install/node.sh)qmd mcp --http --daemon & # 3. restart (a new shell also lazy-starts it)A failed swap can also leave a broken husk — a qmd/ dir with only an empty
node_modules/ plus a dangling bin/qmd symlink — in a different npm prefix
than the one which qmd resolves to (nvm’s). Delete the husk; the live copy is
the one on PATH.