Skip to content
Agentmart
Submit a tool

n8n-code-python

Write Python in n8n Code nodes (native Python, `language` pythonNative, n8n 2.x).

Skill by czlonkowski

IndexedMIT★ 6,517
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/czlonkowski-n8n-code-python

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Write Python in n8n Code nodes (native Python, `language` pythonNative, n8n 2.x). Use when the user explicitly wants Python in a Code node, when migrating old Pyodide/"Python (Beta)" code that used _input/_json/_node/_now, or when a Python Code node fails with NameError, "Security violations detected", "Import of standard library module … is disallowed", "__build_class__ not found", "A 'json' property isn't a dictionary", or "Python runner unavailable". Covers the only two variables (_items/_item), dict-only access, imports blocked by default, the sandbox's denied builtins, accepted return shapes, and how errors interact with onError. JavaScript is the default for Code nodes — native Python has no n8n helpers and, by default, no imports. EXCEPTION — for Python in the AI-agent-callable Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode), use the n8n-code-tool skill instead (input is _query, return must be a string).

Use cases: security, writing, agent-building, code-generation, debugging

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/n8n-code-python

What's inside

Skill name
n8n-code-python
Description
Write Python in n8n Code nodes (native Python, `language` pythonNative, n8n 2.x). Use when the user explicitly wants Python in a Code node, when migrating old Pyodide/"Python (Beta)" code that used _input/_json/_node/_now, or when a Python Code node fails with NameError, "Security violations detected", "Import of standard library module … is disallowed", "__build_class__ not found", "A 'json' property isn't a dictionary", or "Python runner unavailable". Covers the only two variables (_items/_item), dict-only access, imports blocked by default, the sandbox's denied builtins, accepted return shapes, and how errors interact with onError. JavaScript is the default for Code nodes — native Python has no n8n helpers and, by default, no imports. EXCEPTION — for Python in the AI-agent-callable Custom Code Tool (@n8n/n8n-nodes-langchain.toolCode), use the n8n-code-tool skill instead (input is _query, return must be a string).
License
MIT
Instructions
210 lines in SKILL.md · view on GitHub

Files (3)

  • COMMON_PATTERNS.md
  • README.md
  • SKILL.md

SKILL.md (first lines)

# Python Code Node (native)

Since n8n 2.0 the Code node's Python runs as **native Python in a task runner** (`language:
"pythonNative"`). The old Pyodide "Python (Beta)" is gone, and with it every n8n helper it had:
`_input`, `_json`, `_node`, `_now`, `_today` and `_jmespath` all raise `NameError` now. Code copied
from old templates, forum posts or older docs usually breaks on this.

---

## JavaScript first — stronger than before

Native Python gives you two variables and plain Python, and **by default no imports**: no
`json`, `datetime` or `re`. Everything n8n-specific (`$('Node')`, `$jmespath`, Luxon,
`this.helpers.httpRequest`, static data) exists only in JavaScript. Use Python only when the user
explicitly asks for it. Even then, first check whether an expression, Edit Fields, or a native
node (Crypto, Date & Time, HTML, XML) does the job. See **n8n-code-javascript** and the transform
gatekeeper in **n8n-expression-syntax**.

---

## Quick start

```python
# Run Once for All Items (default mode)
return [
    {"json": {"name": it["json"]["name"], "revenue": it["json"]["revenue"]}}
    for it in _items
    if it["json"].get("active")
]
```

```python
# Run Once for Each Item
row = _item["json"]
return {"json": {**row, "name_upper": (row.get("name") or "").upper()}}
```

Node config: `{"language": "pythonNative", "mode": "runOnceForAllItems" | "runOnceForEachItem",
"pythonCode": "..."}` on `n8n-nodes-base.code` typeVersion 2.

---

## The only inputs: `_items` and `_item`

| Mode | Variable | Shape |
|---|---|---|
| Run Once for All Items | `_items` | `list` of plain dicts `{"json": {...}, "pairedItem": {...}}` |
| Run Once for Each Item | `_item` | one plain dict `{"json": {...}, "pairedItem": {...}}` |

- Each variable exists **only in its own mode**. `_items` in each-item mode (or `_item` in
  all-items mode) raises `NameError`.
- **Dict access only.** `it["json"]["name"]` or `it["json"].get("name")`. `it.json.name` raises
  `AttributeError: 'dict' object has no attribute 'json'`.
- **No other nodes.** There is no `_node` / `$('Node')` equivalent. If you need data from another
  branch, bring it in with Merge first, or read it in JavaScript.
- **Webhook payloads** are under `["body"]`: `_items[0]["json"].get("body", {}).get("email")`.
- **Binary data** isn't covered here. Read and write binary in JavaScript (see **n8n-binary-and-data**).
- **pairedItem:** returning `_items` / `_item` keeps it. When you build new dicts and downstream
  uses `$('Node').item`, add `"pairedItem": {"item": i}` to each returned item. That isn't
  verified on native Python yet, so test-run before relying on it.
- Missing keys: prefer `.get(key, default)`. `row["missing"]` raises `KeyError`.

Migration table for legacy code:

| Legacy (Pyodide) | Native |
|---|---|
| `_input.all()` | `_items` |
| `_input.first()["json"]` | `_items[0]["json"]` (guard `if _items`) |
| `_input.item` / `_json` | `_item` / `_item["json"]` |
| `_node["X"]` | not available: Merge upstream, or use JS |
| `_now`, `_today` | not available: pass `{{ $now.toISO() }}` in via Edit Fields, or use JS |
| `_jmespath(data, q)` | not available: `$jmespath` in an expression, or a comprehension |
| `item.json.field` | `item["json"]["field"]` |

---

## Imports: blocked by default

Every `import` (standard library and third-party) is checked against an allowlist **before the
code runs**. The default allowlist is empty, so even `import json` rejects the whole node:

```
Security violations detected
Line 1: Import of standard library module 'json' is disallowed. Allowed stdlib modules: none
```

- **n8n Cloud:** no imports at all.
- **Self-hosted:** the admin can allowlist modules in the task-runner config (see
  **n8n-self-hosting** → `TASK_RUNNERS.md`). Some instances therefore allow `json`, `datetime`
  and `re`, and most don't.
- **Default to import-free code.** If an import would really help, confirm it first: a
  one-line test node `import json` + `return [{"json": {"ok": True}}]`, or ask the user. Never
  assume.
- Without imports: parse JSON strings upstream (`{{ JSON.parse($json.payload) }}` in Edit Fields).
  Do date math with the Date & Time node or Luxon in an expression. ISO-8601 strings still compare and sort correctly as
  plain strings. Use the Crypto node for hashing.
- `requests`, `pandas` and `numpy` are never available unless the admin built a custom runner
  image. Use the HTTP Request node for HTTP.

---

## Sandbox limits (these fail even without imports)

| You write | What happens | Use instead |
|---|---|---|
| `eval`, `exec`, `compile`, `open`, `input`, `type`, `getattr`, `setattr`, `hasattr`, `vars`, `dir`, `globals`, `locals`, `object`, `memoryview`, `breakpoint` | `NameError: name 'type' is not defined` (runtime) | `isinstance(x, dict)`; `key in d` / `d.get(key)` |
| `class Foo: ...` | `__build_class__ not found` (runtime) | dicts + functions |
| `x.__class__`, `"{0.__class__}".format(x)`, `__import__("json")` | `Security violations detected` (whole node rejected before running) | — |
| `global counter` inside a function | `NameError: name 'counter' is not defined`, because your code runs inside a wrapper function | `nonlocal counter` |

Everything else in plain Python works (verified): comprehensions, generators, lambdas, closures,
recursion, `try`/`except`, f-strings / `.format()` / `%`, `sorted`/`min`/`max`/`sum`/`any`/`all`/
`enumerate`/`zip`/`round`, sets, `isinstance`, `print()` (output goes to the browser console).

---

## Return shapes (verified)

Observed on n8n 2.38.5. The auto-wrapping and passthrough behaviours below are undocumented and could

From czlonkowski/n8n-skills, MIT.

More from czlonkowski/n8n-skills

Compare and alternatives

Featured in

Signals

Stars
6,517
Forks
1,075
Open issues
10
Last commit (this folder)
Oct 24, 2026 (3 days ago)
Last release
Oct 24, 2026 (3 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from czlonkowski

Cite this page

n8n-code-python skill. Agent Mart. https://agentmart.dev/skills/czlonkowski-n8n-code-python (last updated 2026-10-24)