Describe the problem/error/question
- When using Task Runners in external mode, the Code node cannot require any module (both built-in like path and external like xlsx). The runner throws “Module ‘???’ is disallowed”.
- The same Code nodes work fine when Task Runners are in internal mode (or when runners are disabled).
What is the error message (if any)?
Examples seen in the UI and execution logs when running a minimal Code node:
- Built-in module:
- Code:
const path = require('path'); return [{ json: { ok: !!path } }]; - Error:
- Code:
{
"errorMessage": "Module 'path' is disallowed [line 1]",
"n8nDetails": {
"stackTrace": [
"Error: Module 'path' is disallowed",
" at /opt/runners/task-runner-javascript/dist/js-task-runner/require-resolver.js:16:27",
" at VmCodeWrapper (evalmachine.<anonymous>:1:243)",
" at evalmachine.<anonymous>:2:2",
" at Script.runInContext (node:vm:149:12)",
" at runInContext (node:vm:301:6)",
" at result (/opt/runners/task-runner-javascript/dist/js-task-runner/js-task-runner.js:165:61)"
]
}
}
- External module:
- Code:
const XLSX = require('xlsx'); return [{ json: { ok: !!XLSX } }]; - Error:
- Code:
{
"errorMessage": "Module 'xlsx' is disallowed [line 1]",
"n8nDetails": {
"stackTrace": [
"Error: Module 'xlsx' is disallowed",
" at /opt/runners/task-runner-javascript/dist/js-task-runner/require-resolver.js:16:27",
" at VmCodeWrapper (evalmachine.<anonymous>:1:243)",
" at evalmachine.<anonymous>:2:2",
" at Script.runInContext (node:vm:149:12)",
" at runInContext (node:vm:301:6)",
" at result (/opt/runners/task-runner-javascript/dist/js-task-runner/js-task-runner.js:165:61)"
]
}
}
- Note: Accessing process is intentionally blocked in the sandbox (ReferenceError: process is not defined), which is expected. The problem is specific to the module allowlist behavior in external mode.
Please share your workflow
Minimal reproduction 1 (built-in module):
Minimal reproduction 2 (external module):
- Install package into the shared n8n data directory:
docker exec -it n8n_app bash -lc ‘npm install --omit=dev --prefix /home/node/.n8n xlsx’ - Workflow:
Share the output returned by the last node
- The node errors with “Module ‘path’ is disallowed” or “Module ‘xlsx’ is disallowed” as shown above.
Additional details, configuration, and logs
-
Both images are on the same version:
- n8n main: n8nio/n8n:1.112.1
- runners: n8nio/runners:1.112.1
-
External mode is enabled and runners connect successfully. Runners logs (debug) show they pass the allowlist variable names to the JS runner:
2025/09/18 06:45:28 DEBUG [launcher:js] Env vars to pass to runner: [GENERIC_TIMEZONE N8N_RUNNERS_TASK_TIMEOUT PATH N8N_RUNNERS_TASK_BROKER_URI N8N_RUNNERS_HEALTH_CHECK_SERVER_ENABLED N8N_RUNNERS_HEALTH_CHECK_SERVER_PORT N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT N8N_RUNNERS_TASK_TIMEOUT N8N_RUNNERS_HEALTH_CHECK_SERVER_HOST NODE_FUNCTION_ALLOW_BUILTIN NODE_FUNCTION_ALLOW_EXTERNAL]
… connects to ws://app:5679 … offers/accepts tasks, launches runner, etc. -
Allowlist environment variables are set on both the n8n main container and the runners container. I also tried a wildcard to allow everything:
- NODE_FUNCTION_ALLOW_EXTERNAL=*
- NODE_FUNCTION_ALLOW_BUILTIN=path,crypto
Still, path and xlsx are disallowed in external mode.
-
External module installation and resolution:
- Installed xlsx into the shared n8n data dir:
npm install --omit=dev --prefix /home/node/.n8n xlsx
npm ls xlsx --prefix /home/node/.n8n → shows [email protected] - The same volume is mounted into the runners container at /home/node/.n8n.
- runners container env includes: NODE_PATH=/home/node/.n8n/node_modules
- Installed xlsx into the shared n8n data dir:
-
Relevant docker-compose excerpts (only the relevant services shown):
app:
image: n8nio/n8n:1.112.1
container_name: n8n_app
networks:
- mcp-server
environment:
- N8N_RUNNERS_ENABLED=true
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
- N8N_RUNNERS_AUTH_TOKEN=REDACTED
- N8N_NATIVE_PYTHON_RUNNER=true
- NODE_FUNCTION_ALLOW_EXTERNAL=*
- NODE_FUNCTION_ALLOW_BUILTIN=path,crypto
- GENERIC_TIMEZONE=Asia/Shanghai
# DB and other envs omitted for brevity
volumes:
- /mnt/user/appdata/n8n/app:/home/node/.n8n
ports:
- "5678:5678"
task-runners:
image: n8nio/runners:1.112.1
container_name: n8n-runners
networks:
- mcp-server
environment:
- N8N_RUNNERS_TASK_BROKER_URI=http://app:5679
- N8N_RUNNERS_AUTH_TOKEN=REDACTED
- NODE_FUNCTION_ALLOW_EXTERNAL=*
- NODE_FUNCTION_ALLOW_BUILTIN=path,crypto
- NODE_PATH=/home/node/.n8n/node_modules
- GENERIC_TIMEZONE=Asia/Shanghai
- N8N_RUNNERS_LAUNCHER_LOG_LEVEL=debug
volumes:
- /mnt/user/appdata/n8n/app:/home/node/.n8n
depends_on:
- app
What I already tried
- Verified both containers run the same version (1.112.1).
- Set the allowlist on both the main n8n container and on the runners container.
- Tried NODE_FUNCTION_ALLOW_EXTERNAL=* to allow everything.
- Installed xlsx into /home/node/.n8n and mounted this volume into runners, setting NODE_PATH=/home/node/.n8n/node_modules.
- External mode still blocks both built-in and external modules with “disallowed”.
- In internal mode, the same Code nodes run fine.
Questions
- Is there a known issue in 1.112.1 with how the broker enforces or passes the module allowlist to external runners?
- Is there a way to verify what allowlist the broker actually sends to the JS task runner?
- Any additional env or config required to allow built-in and external modules in external mode?
Information on your n8n setup
- n8n version: 1.112.1 (Self-hosted)
- Database: Postgres 15
- n8n EXECUTIONS_PROCESS setting: default (own)
- Running n8n via: Docker Compose (Linux host)
- Operating system: Linux (Unraid host)