On managed deployments the import allowlist is not the user's to change, so an
agent that names `N8N_RUNNERS_STDLIB_ALLOW` or tells them their setting is
invalid only produces a support request about something nobody will change for
them. Cloud also runs task runners in external mode, where n8n cannot see the
runner's configuration at all, so that guidance was doubly unfounded there.
Every agent- and user-facing surface now states the capability and stops: assume
no imports, write import-free Python, or use JavaScript. Environment variables
survive only in code comments and the config class, where operators read them.
The external-mode and rejected-allowlist cases now share one message. They led to
the same action already, and the separate wording existed only to name the
setting behind each.
Tests pin the rule across every policy shape rather than the phrasing, so a
future edit cannot quietly reintroduce a config recommendation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The native Python runner's import allowlists are per-deployment and empty by
default, so a fixed "no imports" rule is wrong on an instance that configured
one and a fixed "imports work" rule is wrong everywhere else. Every surface now
reads the configuration and says what is actually true here.
- Add `stdlibAllow` / `externalAllow` to `TaskRunnersConfig`, and spawn the
Python runner from those instead of `process.env`, so the value n8n reports is
the one the runner enforces.
- Derive `pythonImportPolicy` on `InstanceAiContext`. It is authoritative only in
internal runner mode; in external mode the runner is configured separately, so
the policy is marked a guess and the messaging says so.
- `lintPythonCode` takes the policy and only flags imports the runner will
actually reject, naming what is allowed. Without a policy nothing is assumed
allowed, which keeps the sandbox CLI conservative.
- Add a system-prompt section stating the effective policy, and a
`detect-python-code-constraints` host check that re-runs the rules at
build-workflow time with the real allowlist. Informational; it does not block.
- Reword the static surfaces — Code node hints and the workflow-builder skill —
so they describe imports as opt-in per deployment and defer to those two.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Thread the node execution mode into `lintPythonCode` and flag reading the
accessor the runner only defines in the other mode, mirroring the JS rule's
`CODE_MODE_API_MISUSE`.
- Detect relative imports; the runner rejects them with "Relative imports are
disallowed" and the previous root extraction produced no module for them.
- Make the import message module-neutral. It now names both allowlists rather
than reporting the standard-library error for an external package.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The native Python runner allows no imports unless the deployment sets
N8N_RUNNERS_STDLIB_ALLOW, which is empty by default and forced empty in the
official runners image. It also defines only `_items`, `_item` and `print`.
The builder had no way to know either constraint: the `pythonCode` builderHint
covered only network access, and the property description still advertised
`_today` and `_jmespath`, which went away with Pyodide.
- Correct the `pythonCode` description and builderHint, and add a `language`
builderHint that steers to JavaScript unless Python was asked for.
- Add `CODE_NODE_PYTHON_IMPORT` and `CODE_NODE_PYTHON_UNSUPPORTED_GLOBAL` to
the Code node Python linter, so `workflow-sdk validate` reports both defects.
- Note the constraints in the workflow-builder skill.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Github issue / Community forum post (link here to close automatically):
https://github.com/n8n-io/n8n/pull/6348
---------
Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Marcus <marcus@n8n.io>