升級至 2.33.4 後,Code 節點無法解析「xlsx」外部模組

描述問題/錯誤/問題

我在使用 2.33.4 自主託管版本。使用 require('xlsx') 的程式碼節點失敗,出現 Cannot find module 'xlsx' 錯誤。這在我最後一次更新前是可以運作的,更新時間大約在 2026 年 6 月 27–28 日 — 我執行的是 n8nio/n8n:latest 標籤,所以我不再擁有舊版本號,舊映像也已刪除。
NODE_FUNCTION_ALLOW_EXTERNAL=xlsx 一直都有設定,在更新前就可以運作。 該環境變數的歷史比更新早得多,在更新期間沒有更改,現在仍然設定著。唯一改變的是 n8n 版本。

錯誤訊息是什麼(如有)?

Cannot find module 'xlsx'

請分享您的工作流程

{
  "nodes": [
    {
      "parameters": {
        "authentication": "n8nUserAuth",
        "formTitle": "Upload",
        "formFields": {
          "values": [
            {
              "fieldLabel": "File",
              "fieldType": "file"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.6,
      "position": [
        0,
        0
      ],
      "id": "524e6956-6083-4818-80f9-0b7680c5831c",
      "name": "On form submission",
      "webhookId": "bd5787ae-7a59-47ca-913b-a917c7cc56b1"
    },
    {
      "parameters": {
        "jsCode": "// ─── n8n-native way (preferred) ───\nconst { read: xlsxRead, utils: xlsxUtils } = require('xlsx');\n\nconst buffer = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst workbook = xlsxRead(buffer, { type: 'buffer' });\n\nreturn workbook.SheetNames.map((name) => {\n  const sheet = workbook.Sheets[name];\n  const range = sheet['!ref'] ? xlsxUtils.decode_range(sheet['!ref']) : null;\n  const rowCount = range ? range.e.r - range.s.r + 1 : 0;\n  return { json: { name, rowCount } };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        0
      ],
      "id": "014fef67-9f74-4d59-bcf3-35b230fe807a",
      "name": "Extract Sheet Names"
    }
  ],
  "connections": {
    "On form submission": {
      "main": [
        [
          {
            "node": "Extract Sheet Names",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  ],
  "pinData": {},
  "meta": {
    "instanceId": "15ab9a4479ee136cd46352ea8c0166f2b06c66d06e5800486f6e9654739ded93"
  }
}

分享最後一個節點返回的輸出

{"errorMessage":"Cannot find module 'xlsx'\nRequire stack:\n- /usr/local/lib/node_modules/n8n/node_modules/.pnpm/+task-runner@file+packages++task-runner_@opentelemetryopentelemet@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8cy+api@1.9.0_@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/js-task-runner/require-resolver.js\n- /usr/local/lib/node_modules/n8n/no@opentelemetrye_modules/.@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8cnpm/+task-runner@file+packages++task-runner_@opentelemetry+api@1.9.0_@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/js-task-@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8cunner.js\n- /usr/local/lib/node_modules/n8n/node_modules/.pnpm/+task-runner@file+packages++task-runner_@opentelemetry+api@1.9.0_@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/start.js","errorDetails":{},"n8nDetails":{"n8nVersion":"2.33.4 (Cloud)","binaryDataMode":"fi@opentelemetryesystem","@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8ctackTrace":["Error: Cannot find module 'xlsx'","Require stack:","- /usr/local/lib/node_modules/n8n/node_modules/.pnpm/+task-runner@file+packages++task-runner_@o@opentelemetryentelemetry@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8capi@1.9.0_@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/js-task-runner/require-resolver.js","- /usr/local/lib/node_modules/n8n/no@opentelemetrye_modules/.@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8cnpm/+task-runner@file+packages++task-runner_@opentelemetry+api@1.9.0_@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/js-task-runner/js-task-runner.js","- /usr/local/lib/node_modules/n8n/node_modules/.pnpm/+task-runner@file+packages++task-runner_@opentelemetry+api@1.9.0_@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/start.js","    at Module.resolveFilename (node:internal/modules/cjs/loader:1517:15)","    at wrapResolveFilename (node:internal/modules/cjs/loader:1071:27)","    at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1095:10)","    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1122:12)"," @opentelemetry  at Module.load (node:internal/modules/cjs/loader:1294:5)","    at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)","    at Module.require (node:internal/modules/cjs/loader:1617:12)","    at require (node:internal/modules/helpers:153:16)","    at /usr/local/lib/node_modules/n8n/node_modules/.pnpm/+task-runner@file+packages++task-runner@opentelemetry+api@1.9.0@opentelemetry_f4c4f0962cb44afa934cbd57e3ebca8c/node_modules//task-runner/dist/js-task-runner/require-resolver.js:68:26","    at VmCodeWrapper (evalmachine.:2:46)"]}}

關於您的 n8n 設定的資訊

  • n8n 版本: 2.33.4
  • 資料庫(預設:SQLite): PostgreSQL
  • n8n EXECUTIONS_PROCESS 設定(預設:own, main): main
  • 執行 n8n 的方式(Docker、npm、n8n cloud、桌面應用): Docker
  • 作業系統: Ubuntu 24.04

@jburns,在等待回覆的同時,以下是一些可能對你有幫助的資源:

建議的資源

自動符合你的問題。

文件:

論壇:

@ThinkBot@jcuypers@tamy.santos - 你們之前曾幫助過類似的問題,能否看一下?

由 n8n 社群機器人自動建議。這是試用版 - 請在此分享回饋

@jburns 歡迎!

更新您的環境變數

根據下方文件,當任務運行器處於活動狀態時,您必須使用特定的運行器覆蓋來配置外部模組。

Enable modules in Code node | Deploy | n8n Docs..

將下列環境變數新增至您的 docker-compose.yml(或任何定義環境配置的地方),並重新建立容器:

yaml

environment:
  - NODE_FUNCTION_ALLOW_EXTERNAL=xlsx
  - N8N_ENFORCE_SETTINGS_FILE_FOR_RUNNERS=true

謹慎使用程式碼。

或者,如果您在 Docker 中透過各自獨立的容器服務執行任務運行器,外部模組定義變數必須直接應用於任務運行器服務容器環境,而不是主要 n8n 後端容器。

早安 @jburns

請使用穩定版本 n8n 2.31.4 — Stable
如果仍然出現錯誤,請分享以便進行更好的分析。

補充說明:除了在 task runner 環境中應用變數之外,我會避免在此場景中使用 n8nio/n8n:latest,並固定映像的特定版本。這樣,如果更新再次改變 runner 或依賴項的行為,就會更容易準確識別出變更發生在哪個版本中,並進行回滾。

感謝您告訴我們這個問題,我們已建立 CAT-4010 作為內部開發工單來處理它。

@jburns

將你的 compose 檔案更新為類似這樣的內容

services:
  n8n:
    image: n8nio/n8n:latest
    environment:
      - NODE_FUNCTION_ALLOW_EXTERNAL=xlsx
      - NODE_PATH=/usr/local/lib/node_modules
      # ... 你的其他變數
    # 如果你沒有自訂的 Dockerfile,你可能需要在啟動時安裝它:
    command: /bin/sh -c "npm install -g xlsx && n8n"

有幫助嗎?

感謝你的回覆。不幸的是,那不起作用。我沒有任何外部工作執行器,只有一個 n8n 容器。

感謝你的回覆!那個解決方案沒有奏效。它失敗並顯示

Error: Command "/bin/sh" not found

另外,我不記得之前必須安裝 xlsx。提取自檔案或轉換為檔案等原生節點不是已經使用它了嗎?

嗨 Tamy,感謝!我擔心從 2.33.4 回滾到 2.31.4,這對我的執行個體安全嗎?

@jburns

我們不保證 n8n 映像中任何外部套件的可用性。即使它之前可能有效過,那也只是巧合,而不是設計目的。

如上面的訊息所述,你需要自己安裝套件。你可以通過以下任一方式進行:

  1. 透過擴展官方 n8n docker 映像來建立自訂映像,如我們的文檔中所述
  2. 執行一個在啟動 n8n 之前安裝套件的設定命令,如上面所述

我知道這可能有點複雜。從長遠來看,我們計畫對代碼節點進行更改,這樣你就可以直接從工作流程中選擇要使用的依賴項,n8n 將在後台處理它們的安裝。

1.建立一個名為 Dockerfile 的檔案,放在與你的 docker-compose.yml 相同的目錄中:

FROM n8nio/n8n:latest

USER root

# 全域安裝模組
RUN npm install -g xlsx

# 確保為所有程序設定 Node 路徑
ENV NODE_PATH=/usr/local/lib/node_modules

USER node

2.修改你的 docker-compose.yml 以建立此檔案,而不是提取原始映像:

services:
  n8n:
    # 移除或註解掉「image」行
    # image: n8nio/n8n:latest 
    build: .
    environment:
      - NODE_FUNCTION_ALLOW_EXTERNAL=xlsx
      - NODE_PATH=/usr/local/lib/node_modules
      # ... 你的其他變數
    # 移除你之前新增的「command」行

3.部署: 執行下列命令以重新建立並重新啟動:

docker compose up -d --build

這樣有幫助嗎?

@jburns

這是第二個可能修復該問題的修正。

將代碼節點切換到「n8n-native」JavaScript

如果你絕對必須使用 JavaScript,你不再需要 require('xlsx')。n8n 的現代版本提供了一個名為 $binary 的原生全域輔助變數或內置方法來處理檔案數據,無需導入外部套件。

你可以修改代碼以使用內置的視覺輔助 UI 功能或使用:

javascript

// 示例:使用內置 n8n 內部數據參考,不使用 'require'
const binaryData = await this.helpers.getBinaryDataBuffer(0, 'data');
// 使用原生 n8n 陣列項目對應,而不是原始 xlsx 解碼字符串

@jburns
關於回滾的問題,將標籤改回 2.31.4 並不是乾淨的還原方式。2.33.4 已經對你的 Postgres 應用了遷移,而這些遷移只會向前進行,所以舊的二進制檔案將針對一個它從未被設計的資料庫架構啟動。支援的回退方式是還原在更新前拍攝的資料庫快照,由於你從一個你已經沒有的映像進行更新,留在 2.33.4 並將套件安裝到自訂映像中是風險較低的做法。
請看這個:

NODE_FUNCTION_ALLOW_EXTERNAL=xlsx 只是將套件加入允許清單。它不會安裝套件。你的堆疊追蹤到達任務執行器解析器,並在 Cannot find module 'xlsx' 處結束,所以這是模組可用性問題,而不是允許清單問題。

提議的 N8N_ENFORCE_SETTINGS_FILE_FOR_RUNNERS 設定不會安裝 xlsx。啟動命令也會失敗,因為目前的 n8n 映像沒有 /bin/sh。我不會將即時執行個體降級作為下一步測試。

首先釘住目前的 n8n 映像,並備份資料庫以及 n8n 資料磁碟區。針對複製的資料庫和磁碟區測試任何降級,永遠不要針對即時配對進行測試。耐久的生產修正是外部執行器模式,搭配自訂的 n8nio/runners 映像,在 JavaScript 執行器下安裝 xlsx 並在執行器設定中將其加入允許清單。將執行器映像保持在相同的 n8n 版本。

如果你想保持單一容器和內部執行器,目前的文件沒有提供支援的額外依賴映像路徑。改用內建試算表節點或將此解析移至私有自訂節點,而不是仰賴碰巧存在於 latest 中的套件。

非常感謝這個建議,外部執行器方案正是完美的解決方案,我很慶幸沒有走降級的路線。

我最後做的事情:

  1. 固定 n8n 映像檔並在 n8n 停止時歸檔 n8n_data 卷,避免 SQLite 中途寫入:
    docker run --rm -v n8n_data:/data:ro -v "$PWD":/backup alpine tar czf /backup/n8n_data-$(date +%F).tar.gz -C /data .
  2. 將主實例切換為 N8N_RUNNERS_MODE=external 搭配 N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0,並在相同版本標籤上新增 n8nio/runners 邊車
  3. 使用 pnpm add xlsx/opt/runners/task-runner-javascript 下建構自訂執行器映像檔,並在 n8n-task-runners.jsonenv-overrides 區塊中將 xlsx 新增至 NODE_FUNCTION_ALLOW_EXTERNAL。值得注意的是,n8n 容器本身的 NODE_FUNCTION_ALLOW_EXTERNAL 在外部模式下會被忽略——它必須在啟動器配置中。

對於任何擴展映像檔的人來說有個陷阱:建構失敗並出現 ERR_PNPM_UNEXPECTED_STORE。Corepack 拉取最新的 pnpm(11.x,存儲 v11),而基礎映像檔的 node_modules 連結至存儲 v10,pnpm 拒絕混合主要版本。在安裝前進行固定版本即可修復:

ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
RUN corepack prepare pnpm@10.34.5 --activate

不要跟隨錯誤本身的建議去執行 pnpm install——映像檔中沒有鎖檔案,所以它會重新解析整個執行器樹。

有趣的結局:執行器運作後,require('xlsx') 運作正常,但程式碼節點仍拋出空白的「Unknown error」且 errorDetails 為空。結果發現二進制屬性名稱是 File 而非 data,且 getBinaryDataBuffer 在屬性名稱不正確時拋出非 Error 物件,所以沒有訊息能跨越執行器邊界。使用 Object.keys($input.first().binary) 讀取鍵值就解決了。

再次感謝!這個設置比我之前的更穩健,所以無論如何都值得去做。