# n8n Code node: run modes, return shapes, and import restrictions

Debug the n8n Code node by classifying the failure: wrong run mode (Run Once for Each Item vs Run Once for All Items), wrong return shape for the mode, or sandbox/import restriction (JS NODE_FUNCTION_ALLOW_EXTERNAL allowlist; Python Pyodide legacy in 1.x vs N8N_RUNNERS_STDLIB_ALLOW/N8N_RUNNERS_EXTERNAL_ALLOW/N8N_RUNNERS_ALLOW_TRANSITIVE_IMPORTS allowlists in 2.x), plus what the Code node can never do (filesystem, HTTP).

Exact reference: {"kind":"skill_version","skill_id":"skl_gL5zicaTALxbKAmp-9O4xQ","version_id":"skv_JGhk0PaDGqEn2Ue6G2dJog"}

Applicability: [{"constraint":"1.x (Pyodide-based Python Code node; legacy Python mode)","technology":"n8n","version_scheme":"semver"},{"constraint":"2.x (native Python/JS task runners with import allowlists)","technology":"n8n","version_scheme":"semver"}]

# n8n Code node: pick the run mode, shape the return, respect the sandbox

Use this when a Code node returns nothing, returns nested/garbled data, throws
on `import`/`require`, or when Python code that worked on n8n 1.x breaks after
upgrading to 2.x. Most Code-node bugs are one of three distinct problems —
wrong run mode, wrong return shape for the mode, or a sandbox/import restriction —
and this procedure tells them apart before you rewrite the logic.

## 1. Choose the run mode FIRST: per-item vs all-items

The Code node has a **Mode** parameter with two values, and the semantics are
not subtle. Getting this wrong is the #1 cause of Code-node bugs.

- **Run Once for Each Item**: the code runs once per input item. Inside, `$json`
  (JavaScript) refers to the current item only. Best for 1:1 per-item transforms
  with independent logic.
- **Run Once for All Items** (default): the code runs once total and receives
  every input item. Access them with `$input.all()`. Best for anything that looks
  across items: aggregation, filtering, sorting, deduplication, joining,
  batching.

Decision rule: if you need to see more than one item at a time, use All Items
and loop inside the node. If the transformation is strictly per-item and never
compares items, Each Item is fine. When unsure, use All Items — you can always
loop inside, but you cannot reach other items from Each Item mode.

Performance note: Each Item mode pays a per-item execution-context cost, so over
thousands of items it is measurably slower than one All Items run with an
internal loop. Do not use Each Item mode (or a Loop Over Items node around a
Code node) as a substitute for a plain `for` loop inside one All Items node.

## 2. Match the return shape to the mode

The mode also dictates what you must return. Returning the wrong shape is the
classic silent failure: the node "runs" but downstream gets empty or nested
data.

- **All Items mode**: return an **array** of item objects, each shaped
  `{ json: {...}, binary: {...}, pairedItem: {...} }` (binary and pairedItem
  only when needed).
- **Each Item mode**: return a **single** item object `{ json: {...} }` from
  each invocation — n8n collects them into the output array for you. Returning
  an array here nests an array inside the item instead of emitting items.

```javascript
// All Items: transform every item in one run
const items = $input.all();
return items.map((item, index) => ({
  json: { ...item.json, total: item.json.price * item.json.qty },
  pairedItem: { item: index }
}));
```

```javascript
// Each Item: one item in, one item out
return {
  json: { ...$json, total: $json.price * $json.qty }
};
```

Item-linking (`pairedItem`) is what lets downstream expressions trace which input
item produced which output item; breaking it is a separate failure class from
getting the mode/shape wrong, so fix the mode and shape first.

## 3. JavaScript imports: built-ins are fine, externals are allowlisted

The JS Code node runs sandboxed. Built-in Node.js modules are available via
`require`. External npm packages are not available by default, and there is no
way to `npm install` from inside the node. To use one:

1. Install the package where the n8n process (or the task runner, with n8n 2.x
   task runners enabled) can resolve it.
2. Add it to the `NODE_FUNCTION_ALLOW_EXTERNAL` environment variable
   (comma-separated package names) on n8n startup.

Both steps are required; a missing allowlist entry fails with an access error
even if the package is installed. Related: `NODE_FUNCTION_ALLOW_BUILTIN`
controls which Node.js built-ins are importable.

## 4. Python: version decides what is even possible

Python support in the Code node changed completely between major versions — this
is the first thing to check when Python code breaks.

- **n8n 1.x**: Python runs on **Pyodide**, a WebAssembly port of CPython. Only
  packages included with Pyodide are importable, Python runs slower than
  JavaScript, and it is legacy: **n8n v2 removed Pyodide support**.
- **n8n 2.x (native Python task runner)**: imports are blocked by default with
  `Security violations detected` / `Import of standard library module 're' is
  disallowed. Allowed stdlib modules: none`. Imports are controlled by
  allowlists, set as environment variables (comma-separated, `*` allows all):
  - `N8N_RUNNERS_STDLIB_ALLOW` — Python standard library modules
  - `N8N_RUNNERS_EXTERNAL_ALLOW` — third-party packages
  - `N8N_RUNNERS_ALLOW_TRANSITIVE_IMPORTS` — boolean, default false; when true,
    an allowlisted package's own internal imports skip the allowlist (needed for
    packages with large dependency trees like pandas or boto3; the Code node's
    own imports are still checked)
- Third-party packages must additionally be **installed into the runner image**:
  extend the `n8nio/runners` image and install into
  `/opt/runners/task-runner-python` (e.g. with `uv pip install`). An allowlist
  entry alone fails if the package is not installed.
- With external task-runner mode, the allowlist variables belong in the runner
  configuration (the runner container's environment / config file), and both the
  n8n container and the runner share an auth token, set once as a long random
  value in both places.

Version sniffing: if the node offers "Python (Beta)" with `_input`/`_json`
helpers, you are on the 1.x Pyodide lineage; if imports fail with "Security
violations detected", you are on the 2.x native runner and need the allowlists.

## 5. What the Code node cannot do

Regardless of version, the Code node cannot access the file system and cannot
make HTTP requests directly. Do not fight the sandbox:

- File reads/writes → the Read/Write Files from Disk node.
- HTTP calls → the HTTP Request node.
- Needing a library that cannot be allowlisted → move the logic to an Execute
  Command node, an external service called via HTTP, or a self-hosted runner
  image with the dependency baked in.

## 6. Checklist

1. Mode matches the logic: cross-item → All Items; 1:1 → Each Item.
2. Return shape matches the mode: array of items for All Items, single object
   for Each Item.
3. JS `require` fails → check the package is installed where the runner resolves
   and named in `NODE_FUNCTION_ALLOW_EXTERNAL`.
4. Python `import` fails on 2.x → add it to `N8N_RUNNERS_STDLIB_ALLOW` or
   `N8N_RUNNERS_EXTERNAL_ALLOW` (and install third-party packages into the
   runner image); transitive-import failures → `N8N_RUNNERS_ALLOW_TRANSITIVE_IMPORTS=true`.
5. Python worked on 1.x, broke on 2.x → Pyodide was removed; migrate to the
   native runner allowlist model.
6. Filesystem or HTTP from inside the node → use the dedicated nodes instead.


## Supporting basis and limitations

Built from the official n8n Code node documentation (run modes, built-in methods, filesystem/HTTP restrictions, Pyodide legacy status in 1.x and removal in v2), recurring community threads on Python import blocks in n8n 2.x ("Security violations detected", "Allowed stdlib modules: none"), the n8n-docs PR documenting N8N_RUNNERS_ALLOW_TRANSITIVE_IMPORTS, and community guides on extending the n8nio/runners image with NODE_FUNCTION_ALLOW_EXTERNAL / N8N_RUNNERS_STDLIB_ALLOW / N8N_RUNNERS_EXTERNAL_ALLOW.

## Change and rationale

New skill: n8n Code node run-mode selection, return-shape rules, and JS/Python import restrictions.

Code-node questions are among the most common n8n support threads and cluster into three confusable failure modes (run mode, return shape, import restrictions), with the 1.x-to-2.x Python change (Pyodide removal, new import allowlists) breaking previously working workflows. A decision procedure that separates the three before prescribing fixes prevents rewriting working logic and documents the exact allowlist variables users actually need.
