在 Windows 底下用 npm build 產生專案發佈檔案時,碰到一個錯誤:

EPERM: operation not permitted unlink…

但最後用了一個好像別人還沒提過的解法,決定紀錄一下,以後碰到時可以再回來翻。

背景資訊:
– 這個專案並不是第一次執行建置動作,至少近幾個月每次改都好好的。
– 沒有變動原有的程式邏輯,主要只是一些圖文修改。
– dev/start 在本機是可以正常跑起來的。

建置失敗,當然就無法產生可以上傳的檔案,為了任務的遂行,最快的方式可能是換另一台電腦來跑,或是改成在雲端做 CI/CD,但感覺治標不治本。

stack trace 也沒有提供什麼有用的訊息,都斷在 node_modules 裡面一些 npm 套件的檔案,而不是專案本身的檔案。

科技的發展就是使用者只要打開水龍頭,水就會流出來;打開開關,燈就會亮,而不用管牆壁後面有什麼,理解發電廠、自來水廠到開關中間發生了什麼事。node_modules 裡面的東西沒事不會去查修的,這大概就像麥當勞今天的薯條炸得不好吃,就飛去美國要求看生產履歷和去農場現場勘查…這不是普通人類能做的事。

網路上搜尋 operation not permitted unlink 的文章,至少有 60 幾萬筆搜尋結果。這好像不是太冷門的問題,大致有幾種大方向:

一、Node.js 和 npm 有問題系列

這個沒有解決我的問題。
這個思路就是環境中的 node 和 npm 發生了不知道什麼問題,解法是把環境更新或是砍掉重練。
通常伴隨以下步驟:

  • npm cache clean --force 清除快取。
  • 刪掉專案內的 node_modules 資料夾然後 npm i 重裝。
  • npm install -g npm@latest --force 重裝最新的環境。
  • npm update 相關指令升級專案內的套件,搞不好升級就好了?
  • 安裝 nvm (Node Version Manager),用 nvm use 嘗試在不同的版本之間切換,看能不能找到一個版本號是可以正常建置的。
  • 解除安裝 Node.js,把 %USERPROFILE%\AppData\Roaming 中的 npm 資料夾刪除,重新開機後再次安裝。

某些操作在不同作業系統又略有差異,例如 sudo 或 rm -rf 這都沒有我們 Windows 的事情。
除了解除安裝跟重開機,其他差不多都試過了,那些 npm install, update 都非常花時間,而且最後也沒解決問題。

雖然 npm update 之後 build 的畫面長不一樣了,但錯誤訊息還是一模一樣。

想更新也不是隨便就能成功的,可能出現 ERESOLVE unable to resolve dependency tree 之類千奇百怪的錯誤訊息,有時候要靠 --legacy-peer-deps 參數或其他方法才能更新。
或是成功更新,但執行後專案原地爆炸,可能裡面的相依套件不一定有跟上,或是新版本有新問題,網路上有一些使用 ncu(npm-check-updates) 之類的技巧,不在本文討論範圍。

二、權限不足系列

這個也沒有解決我的問題。
這個思路就是使用者的權限不足,需要安裝或增刪檔案無法正常操作,具體作法包括檢查 nodejs 的各種目錄。

1.資料夾權限不足

1.在 Windows 的執行輸入 sysdm.cpl,會叫出系統內容,
2.然後到「環境變數」中找到 Node.js 的各種目錄,
3.在那些資料夾按右鍵>內容>安全性,檢查目前電腦登入的使用者或群組,資料夾權限是否為完全控制? 或是有一些奇怪的權限沒勾到?

2.Terminal 工具權限不足

看平常是用什麼 terminal 工具來執行 npm xxx,
先把程式關掉,然後在開始選單或安裝位置找到程式,按滑鼠右鍵使用「以系統管理員身分執行」。

不管是使用 cmd (命令提示字元),或是使用 Visual Code Code 的終端機工具列,還是使用 Windows Terminal,應該基本上都有相同的功能。

不過我這是個人電腦,不是用遠端連到廠商的伺服器,最近也沒變更系統設定,怎麼會有權限不足這回事?
當然也是沒效。

三、網路有問題系列

這個也沒有解決我的問題。
例如自己網路的問題,如 4G/5G網路不穩、在某些有鎖外網的上網,或是在大陸被牆。
或是遠端網路的問題,如 NPM 伺服器故障等原因,不過通常這些會伴隨 connect ETIMEDOUT 之類的錯誤訊息。

1.如果是怕 npm 官方伺服器故障之類的,可以到 npm status 查看系統狀態。
2.有的討論文章則是改用 pnpm 就迎刃而解,原因不明。
3.如果網路環境有鎖 npm,可以使用 npm config set registry https://xxx... 搭配其他來源:
npm(預設) https://registry.npmjs.org/
yarn https://registry.yarnpkg.com/
taobao https://registry.npm.taobao.org/
npmMirror https://skimdb.npmjs.com/registry/

但我的網路好好的,其他專案也能正常運作,應該與此無關。

四、檔案有問題系列

檔案可能某些原因被防毒軟體或防火牆阻擋,或是遭遇 os 的檔案系統問題,處理方式包含:

  • 關閉電腦的防毒軟體
  • 關閉電腦的防火牆
  • 如果是用 Windows 內建的防毒軟體,可以參考 Add an exclusion to Windows Security 把專案目錄加到排除清單。
  • 檔案在外接硬碟,USB 已經進入薛丁格的連接狀態。把外接硬碟拔掉重插,或是檢查電量是否足夠,筆電的話把電插上。
  • 檢查檔案目錄路徑是否太長
    例如 D:/…… 接到最後的檔案名稱,整串太長。( Windows 的路徑長度上限 為 260 個字元)。
    可能在 NAS 或是一些複雜的使用者管理工具容易碰上資料夾放太多層、檔名太長的。
    如果檔案刪不掉,可以安裝 rimraf 然後用指令去刪,或是從 7-Zip 的那個檔案瀏覽介面找到檔案,然後按 shift+delete 刪除。

檢查檔案是否被其他程式占用

回到一開始的錯誤訊息,無法 unlink 的檔案其實是一張 svg 圖片,但 build 的程式裡面並沒有牽涉圖片轉檔或壓縮相關的,難道是圖片有問題?
但之前都好好的,繪圖軟體存出來的會有什麼問題…

於是修改網頁程式,把使用到那張圖片的部分註解掉…結果沒效。
放一張新的圖檔進去,然後把引用的圖片路徑換成新圖檔的位置…結果沒效,build 時還是卡在舊的圖檔。
嘗試把舊的圖檔刪掉,結果出現使用 Windows 系統的人可能都看過的「檔案正在使用中」訊息,看來這才是真正的兇手

如果有顯示是什麼程式在佔用,就直接打開工作管理員把程序結束掉。

但沒有顯示的話,還是打開工作管理員
1.切換到「效能」分頁
2.點擊底下的「開啟資源監視器」
3.切換到 CPU 分頁底下
4.找到「關聯控制代碼」,在右邊的搜尋控制代碼把檔案名稱打上去。
5.稍等一下子,會跑出哪個程序正在使用這支檔案,像本例事件,兇手是 dllhost.exe。
6.按滑鼠右鍵,選擇「結束處理程序」。

然後 npm build 就恢復正常了。

總結

所以 build 時出錯,有時候原因跟 npm 套件版本或專案寫的程式無關,就是檔案被鎖定住了,建置工具也無法顯示更簡單易懂的訊息。

dllhost.exe 是 system32 底下的 Windows 系統正常文件,但不知道為什麼他會佔住圖片。
但讓我想到電腦之前有安裝可以讓 svg 直接顯示縮圖的工具,偶爾會當掉,當掉之後,只要資料夾的要顯示縮圖的地方都會卡住,通常電腦重開機就會好,看來 operation not permitted unlink 也只是它當掉的副作用罷了。