pnpm install 出現 Operation not permitted?先查 hardlink 與 clone
pnpm install 出現 Operation not permitted 時,錯誤位置常在 node_modules,但原因未必是「目前使用者沒有讀寫專案目錄」。pnpm 可能正在把內容從 store 以 hardlink 或 copy-on-write clone 匯入,而目前的檔案系統、容器權限或掛載方式拒絕了那個操作。
pnpm 12.4.1 在 2026 年 9 月 10 日修正了這條路徑:packageImportMethod 設為 auto 或 clone-or-copy 時,hard link/COW clone 被檔案系統拒絕,會改用 copy。明確指定 hardlink 或 clone 時,仍會保留錯誤,因為那代表專案刻意要求特定的匯入方式。
所以第一個判斷不是刪除 node_modules,而是先確認 pnpm 版本、匯入策略和 store 是否位於另一個檔案系統。
先把錯誤分成四種
| 症狀 | 較可能的原因 | 第一個動作 |
|---|---|---|
| Operation not permitted,發生在匯入 store 的檔案 | hardlink 或 COW clone 被檔案系統拒絕 | 查 pnpm 版本與 packageImportMethod |
| 明確使用 hardlink 或 clone 時持續失敗 | 設定要求的能力不存在,fallback 不會介入 | 改成 auto 或 copy,或把 store/專案移到合適的檔案系統 |
| Docker 建置時出現 Invalid cross-device link | nested node_modules 保留操作跨越 mount 或 layer | 先升級到 pnpm 12.4.1,再檢查 store 與 build layer |
| Windows 顯示 Access denied 且重試很久 | 檔案鎖定或暫時性權限競爭 | 更新 pnpm,再找出佔用檔案的程序 |
同一個 Operation not permitted 文字可能來自不同作業系統。不要只複製最後一行錯誤給別人,至少要保留完整命令、pnpm 版本、Node 版本、執行環境和 packageImportMethod。
先收集可比對的環境資訊
在失敗的同一個專案目錄執行:
pnpm --versionnode --versionpnpm config get --location=project packageImportMethodpnpm config get store-dirpnpm config list --location=project再檢查專案設定是否有明確要求特殊匯入方式:
rg -n 'packageImportMethod|nodeLinker|storeDir' pnpm-workspace.yaml package.json 2>/dev/null || true最後一個命令若某個檔案不存在會回傳錯誤,不代表 pnpm install 的原因;重點是找出是否存在 pnpm-workspace.yaml 或其他專案設定。也要確認 store 和專案是不是位於不同 volume、bind mount、Docker layer、網路磁碟或特殊 checkout。
packageImportMethod 應該怎麼選
常見設定可以先這樣理解:
| 值 | 行為 | 適合情境 |
|---|---|---|
| auto | 依作業系統與檔案系統選擇可行方式 | 一般本機、CI 與不確定的容器環境 |
| copy | 直接複製檔案,較少依賴檔案系統能力 | rootless container、特殊 checkout、排錯或可接受較多 I/O |
| hardlink | 要求 hard link,節省空間但需要相容的 store 與檔案系統 | 已確認同一檔案系統且需要 inode 共用 |
| clone-or-copy | 先嘗試 COW clone,失敗再複製 | 支援 reflink 的環境,且不希望 clone 失敗就中止 |
pnpm 12 的專案設定放在 workspace 根目錄的 pnpm-workspace.yaml:
packageImportMethod: auto如果你的部署環境已知不允許 hardlink 或 reflink,可以在該環境明確使用:
packageImportMethod: copy這個選擇是效能與相容性的取捨,不是單純的「哪個比較新」。copy 可能增加安裝時間與磁碟使用量,但比讓 CI 因為一項不必要的檔案系統能力而失敗更容易預測。若團隊依賴 hardlink 的空間效率,應修正 store 和專案的掛載位置,而不是默默改成 copy。
pnpm 12.4.1 會自動 fallback 的邊界
官方 12.4.1 release note 明確涵蓋下列案例:
- EdenFS checkout 沒有 hard link 時,auto 或 clone-or-copy 會改用 copy。
- rootless container 拒絕 clone syscall 時,auto 或 clone-or-copy 會改用 copy。
- Android 上拒絕 hardlink 或 reflink 的檔案系統也會改用 copy。
- Docker 建置保留套件內 nested node_modules 時,不再因 Invalid cross-device link 直接失敗。
但以下情況仍不會被這個修正掩蓋:
- 你明確設定 packageImportMethod: hardlink 或 clone。
- 失敗其實是 store、node_modules 或專案目錄的傳統讀寫權限。
- registry、憑證、生命週期腳本或套件本身的 build script 失敗。
- 磁碟已滿、inode 用盡、唯讀 mount 或容器的 quota 已達上限。
因此「升級到 12.4.1」是第一個可驗證的修正,不是所有權限錯誤的萬用解答。
rootless container 和 Docker 的排錯順序
容器內最容易混淆的是:pnpm 可以讀取 store,不代表核心允許它對 store 做 hardlink 或 clone。先在相同容器和相同使用者下確認設定,再決定是否使用 copy:
pnpm config get store-dirpnpm config get --location=project packageImportMethodpnpm install --frozen-lockfile如果目前是明確的 hardlink 或 clone,先在可拋棄的 build job 改成 auto;rootless 環境仍失敗時再改成 copy。若 store 透過 bind mount 放在另一個 volume,也要把它當成獨立變數測試,不要只改檔案 owner。
Dockerfile 不需要為了這個錯誤先加入 chmod 777 或以 root 執行整個 install。先固定 Node、pnpm、store 路徑和 mount,再比較 cold install、warm install 與 frozen install。若只是 nested node_modules 的跨裝置保留錯誤,12.4.1 的修正應先被納入測試矩陣;如果仍失敗,再縮小到最小套件和最小 Docker layer。
Windows 與一般檔案權限不要混為一談
12.4.1 也調整了 Windows 檔案操作的權限錯誤重試,但 Access denied 仍可能是編輯器、測試程序、病毒掃描器或另一個 pnpm 行程持有檔案。先關閉會寫入 node_modules 的程序,再重現一次;若只有特定檔案被佔用,保留檔名和程序資訊。
若錯誤發生在刪除舊 node_modules,新的錯誤訊息可能會透過 ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR 指出無法清理的實際檔案或目錄。這比直接整棵刪掉更適合拿來定位問題。
驗證修正,不要只看 install 變綠
完成版本或設定變更後,在乾淨工作副本跑一次:
pnpm --versionpnpm install --frozen-lockfilegit diff --exit-code -- pnpm-lock.yamlpnpm testpnpm build若專案沒有 test 或 build script,請換成實際的 lint、型別檢查和打包命令。要另外記錄:
- cold store 和 warm store 是否都成功。
- 本機與 CI 是否使用同一個 packageImportMethod。
- node_modules 是否真的由預期的使用者建立。
- install 是否執行了 build script,以及產物是否可被讀取。
- 變更是否只是 package manager 設定,而不是順便重寫 lockfile。
不要用一次成功的 warm install 證明環境已修好;store 已經有檔案時,可能沒有走到原本失敗的匯入路徑。
這三個動作不要當成第一個修正
- 不要用 sudo 執行專案 install,再把 root 建立的 node_modules 留給一般使用者。
- 不要把整個專案或 node_modules chmod 777,這會掩蓋 mount、owner 和程序鎖定問題。
- 不要先刪除 store、lockfile 和 Docker cache;先保存錯誤與設定,否則無法知道是哪個變數改變。
若要清理,先在可回復的分支或 disposable runner 做,並只清理已確認的 node_modules 或 store 路徑。不同問題應保留不同的最小重現,不要用一次大清理取代診斷。
結論:先讓 pnpm 選擇可行的匯入方式
pnpm install 的 Operation not permitted 若出現在 hardlink 或 COW clone 路徑,常常是檔案系統能力和設定的組合問題。pnpm 12.4.1 已讓 auto 與 clone-or-copy 在多個受限環境 fallback 到 copy,但明確的 hardlink、clone 和一般檔案權限錯誤仍要分開處理。
最短排錯路徑是:記錄 pnpm/Node/store、查 packageImportMethod、在相同容器或 CI runner 重現,再用 frozen install、測試和 build 驗證。先修正一個變數,才知道真正解決的是檔案系統問題還是剛好換了環境。
常見問題
Q: pnpm 12.4.1 會修好所有 Operation not permitted 嗎?
A: 不會。它主要修正 hardlink 或 COW clone 被拒絕時的匯入 fallback。若是唯讀 mount、磁碟 quota、一般 owner、檔案鎖定或生命週期腳本失敗,仍要依原始錯誤排查。
Q: 已經使用 packageImportMethod: hardlink,應該直接改成 copy 嗎?
A: 先確認 hardlink 是否為必要的空間最佳化。如果只是想讓 rootless CI 通過,可以在該環境用 auto 或 copy;如果 production 依賴 hardlink,則應修正 store/專案的檔案系統與掛載方式,並保留兩種環境的測試結果。
Q: 可以刪除 pnpm-lock.yaml 重新安裝嗎?
A: 不要把刪 lockfile 當成檔案系統錯誤的修正。先用既有 lockfile 做 frozen install;只有在已理解依賴變更且有隔離分支時,才重新解析並逐項審查 diff。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。