n8n Workflow & Code Review Agent
Run this checklist against ANY n8n workflow JSON, custom node code, or deployment configuration to catch errors before they reach production.
Quick Reference — Review Areas
| Area |
Critical Checks |
Reference |
| Workflow JSON |
IConnections 3-level nesting, unique node names, required fields |
methods.md |
| Connection Wiring |
Type matching, correct indices, no orphan nodes |
methods.md |
| Expressions |
Valid variable refs, context restrictions, JMESPath order |
methods.md |
| Credentials |
ICredentialType completeness, authenticate method, test endpoint |
methods.md |
| Node Types |
INodeType interface, execute return type, property types |
methods.md |
| Error Handling |
Error workflow, continueOnFail, retry config |
methods.md |
| Deployment |
Encryption key, queue mode, volume mounts, PostgreSQL |
methods.md |
| Code Node |
Return format, restricted variables, sandbox limits |
methods.md |
| Security |
No hardcoded secrets, encryption, task runners |
methods.md |
| Anti-Patterns |
Consolidated list from all skill areas |
anti-patterns.md |
Decision Tree — Review Workflow
START: What are you reviewing?
├─ Workflow JSON file (.json)
│ ├─ Run: Workflow JSON checks (Section 1)
│ ├─ Run: Connection Wiring checks (Section 2)
│ ├─ Run: Expression checks on all parameter values (Section 3)
│ ├─ Run: Error Handling checks (Section 6)
│ └─ Run: Anti-Pattern scan (Section 10)
│
├─ Custom node code (.node.ts)
│ ├─ Run: Node Type checks (Section 5)
│ ├─ Run: Credential checks if node uses credentials (Section 4)
│ ├─ Run: Error Handling checks (Section 6)
│ └─ Run: Anti-Pattern scan (Section 10)
│
├─ Credential definition (.credentials.ts)
│ └─ Run: Credential checks (Section 4)
│
├─ Code node content
│ ├─ Run: Code Node checks (Section 8)
│ └─ Run: Anti-Pattern scan (Section 10)
│
├─ Deployment config (docker-compose.yml / env vars)
│ ├─ Run: Deployment checks (Section 7)
│ └─ Run: Security checks (Section 9)
│
└─ Full project audit
└─ Run ALL sections sequentially
1. Workflow JSON Validation
ALWAYS verify these required fields on every node in nodes[]:
| Field |
Type |
Rule |
id |
string |
MUST be unique UUID |
name |
string |
MUST be unique within the workflow |
type |
string |
MUST match a registered node type (e.g., n8n-nodes-base.httpRequest) |
typeVersion |
number |
MUST be a valid version for the node type |
position |
[number, number] |
MUST be [x, y] coordinate array |
parameters |
object |
MUST exist (can be empty {}) |
ALWAYS verify the workflow root object contains:
id (string)
name (string)
active (boolean)
nodes (array)
connections (object)
NEVER accept a workflow where two nodes share the same name — connections reference nodes by name, so duplicates break wiring.
2. Connection Wiring Validation
IConnections uses 3-level nesting:
connections[sourceNodeName][connectionType][outputIndex] = IConnection[]
ALWAYS verify:
- Every key in
connections matches a name in nodes[]
- Every
IConnection.node value matches a name in nodes[]
IConnection.type is a valid NodeConnectionType (usually "main")
IConnection.index does not exceed the destination node's input count
- Trigger nodes (
group: ['trigger']) have NO incoming connections
- Non-trigger nodes have at least one incoming connection (unless intentionally orphaned)
- Multi-output nodes (IF, Switch) have the correct number of output arrays
IF node pattern: connections["IF"].main MUST have exactly 2 arrays — index 0 for true, index 1 for false.
3. Expression Validation
ALWAYS verify expressions ({{ ... }}) use correct variable references:
| Context |
Available |
NOT Available |
| Any expression |
$json, $binary, $input, $execution, $workflow, $now, $today, $env, $vars, $prevNode, $runIndex, $parameter |
— |
| Code node |
All $ vars except $itemIndex and $secrets |
$itemIndex, $secrets |
| Python Code node |
_ prefix versions (_json, _items) |
$ prefix, dot notation on items |
ALWAYS verify $jmespath(object, searchString) parameter order — object FIRST, search string SECOND. This differs from the JMESPath spec.
NEVER allow $("<NodeName>") to reference a node name that does not exist in the workflow.
4. Credential Validation
ALWAYS verify ICredentialType implementations include:
| Property |
Required |
Rule |
name |
YES |
Internal identifier, matches node's credential reference |
displayName |
YES |
Human-readable label |
properties |
YES |
Array of INodeProperties[] defining input fields |
authenticate |
YES |
Method with type: 'generic' and properties object |
test |
RECOMMENDED |
ICredentialTestRequest with test endpoint |
ALWAYS verify authenticate.type is 'generic' — other values are not supported.
ALWAYS verify credential expressions use $credentials prefix: ={{$credentials.apiKey}}.
NEVER allow credentials to be hardcoded in node parameters — ALWAYS use credential references.
5. Node Type Validation
ALWAYS verify INodeType implementations:
| Check |
Expected |
Common Failure |
description property |
INodeTypeDescription with all required fields |
Missing inputs, outputs, or properties |
execute() return type |
Promise<INodeExecutionData[][]> |
Returning single array [] instead of [[]] |
| Trigger nodes |
inputs: [] and group: ['trigger'] |
Having inputs on trigger nodes |
Property type values |
Valid NodePropertyTypes |
Using invalid type strings |
displayOptions |
References existing property names/values |
Referencing non-existent parameters |
credentials array |
Each entry has name matching a credential type |
Credential name mismatch |
ALWAYS verify execute() returns [returnData] (wrapped in outer array), NOT just returnData.
ALWAYS verify each item in the return array has a json property: { json: { ... } }.
6. Error Handling Validation
ALWAYS verify:
- Error workflow configured —
settings.errorWorkflow is set in production workflows
- continueOnFail pattern — nodes using
this.continueOnFail() include error data in output:returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
- Retry on transient failures — HTTP/API nodes set
retryOnFail: true, maxTries >= 2, waitBetweenTries >= 1000
- Error node exists — at least one Error Trigger workflow is available for the instance
onError setting — nodes specify behavior: 'continueErrorOutput', 'continueRegularOutput', or 'stopWorkflow'
NEVER allow a production workflow without an error workflow — silent failures are unacceptable.
7. Deployment Validation
ALWAYS verify for production deployments:
| Check |
Expected |
Consequence of Missing |
N8N_ENCRYPTION_KEY |
Explicitly set and backed up |
Key regeneration locks out all credentials |
NODE_ENV |
production |
Missing production optimizations |
N8N_PROTOCOL |
https |
Credentials transmitted in cleartext |
WEBHOOK_URL |
Set to public URL |
Webhooks unreachable externally |
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS |
true |
Settings file readable by other users |
N8N_RUNNERS_ENABLED |
true |
Code runs in main process (security risk) |
Volume: /home/node/.n8n |
Mounted to persistent volume |
Data lost on container restart |
Queue mode additional requirements:
| Check |
Expected |
Consequence of Missing |
DB_TYPE |
postgresdb |
SQLite does NOT support queue mode |
EXECUTIONS_MODE |
queue |
Workers will not process jobs |
| Redis configured |
QUEUE_BULL_REDIS_HOST + port |
Queue has no broker |
Shared N8N_ENCRYPTION_KEY |
Same key on main + all workers |
Credential decryption fails |
| S3 binary storage |
Configured for shared access |
Binary data inaccessible across instances |
8. Code Node Validation
ALWAYS verify Code node content:
| Check |
Rule |
| Return format (all items) |
MUST return [{json: {...}}, ...] — array of objects with json key |
| Return format (each item) |
MUST return {json: {...}} — single object with json key |
No $itemIndex |
NEVER use $itemIndex in Code node — it is not available |
No $secrets |
NEVER use $secrets in Code node — it is not available |
| No HTTP requests |
NEVER make HTTP calls in Code node — use HTTP Request node |
| No file system access |
NEVER access files directly — use Read/Write Files nodes |
| Python bracket notation |
ALWAYS use item["json"]["field"], NEVER item.json.field in Python |
| Binary data access |
ALWAYS use this.helpers.getBinaryDataBuffer(), NEVER direct buffer access |
9. Security Validation
ALWAYS verify:
- No hardcoded credentials — API keys, tokens, passwords NEVER in node parameters or Code node
- Encryption key set —
N8N_ENCRYPTION_KEY is explicitly configured (not auto-generated)
- Task runners enabled —
N8N_RUNNERS_ENABLED=true (isolates Code node execution)
- File access restricted —
N8N_RESTRICT_FILE_ACCESS_TO limits filesystem paths
- Settings permissions —
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- Env access blocked —
N8N_BLOCK_ENV_ACCESS_IN_NODE=true if env vars contain secrets
- Webhook authentication — production webhooks use Basic Auth, Header Auth, or JWT
- HTTPS enforced —
N8N_PROTOCOL=https with valid TLS termination
- Secure cookies —
N8N_SECURE_COOKIE=true in HTTPS deployments
10. Anti-Pattern Detection
Scan for ALL anti-patterns listed in anti-patterns.md. Key categories:
- Expression anti-patterns: Wrong variable context, reversed JMESPath args,
new Date() instead of Luxon
- Code node anti-patterns: Restricted variables, wrong return format, direct binary access
- Credential anti-patterns: Hardcoded secrets, missing test endpoint, wrong authenticate type
- Deployment anti-patterns: Missing encryption key, SQLite in production, no volume mounts
- Workflow anti-patterns: No error workflow, duplicate node names, orphan nodes
Review Report Template
After completing all applicable checks, produce a report:
## n8n Review Report
**Target**: [filename or description]
**Type**: [Workflow JSON | Custom Node | Credential | Deployment Config | Code Node]
**Date**: [date]
### Summary
- Total checks: [N]
- Passed: [N]
- Failed: [N]
- Warnings: [N]
### Critical Failures
1. [Area] — [What failed] — [Expected state] — [How to fix]
### Warnings
1. [Area] — [What to improve] — [Recommendation]
### Anti-Patterns Detected
1. [AP-XXX] — [Description] — [Location in code/config]
Reference Links
- Validation Methods (Complete Checklist)
- Review Examples (Good/Bad/Fix)
- Anti-Pattern Catalog
1---2name: n8n-agents-review3description: Use when reviewing n8n workflows or validating workflow JSON before deployment. Prevents production errors by catching anti-patterns in node configuration, connection wiring, and expression syntax. Covers workflow JSON structure, node configuration, connection wiring, expression syntax, credential setup, error handling patterns, deployment configuration, and known anti-patterns. Keywords: n8n, review, validation, workflow, audit, anti-pattern.4license: MIT5---6
7# n8n Workflow & Code Review Agent
8
9> Run this checklist against ANY n8n workflow JSON, custom node code, or deployment configuration to catch errors before they reach production.
10
11## Quick Reference — Review Areas
12
13| Area | Critical Checks | Reference |
14|------|----------------|-----------|
15| Workflow JSON | IConnections 3-level nesting, unique node names, required fields | [methods.md](references/methods.md#1-workflow-json-validation) |
16| Connection Wiring | Type matching, correct indices, no orphan nodes | [methods.md](references/methods.md#2-connection-wiring) |
17| Expressions | Valid variable refs, context restrictions, JMESPath order | [methods.md](references/methods.md#3-expression-validation) |
18| Credentials | ICredentialType completeness, authenticate method, test endpoint | [methods.md](references/methods.md#4-credential-validation) |
19| Node Types | INodeType interface, execute return type, property types | [methods.md](references/methods.md#5-node-type-validation) |
20| Error Handling | Error workflow, continueOnFail, retry config | [methods.md](references/methods.md#6-error-handling) |
21| Deployment | Encryption key, queue mode, volume mounts, PostgreSQL | [methods.md](references/methods.md#7-deployment-validation) |
22| Code Node | Return format, restricted variables, sandbox limits | [methods.md](references/methods.md#8-code-node-validation) |
23| Security | No hardcoded secrets, encryption, task runners | [methods.md](references/methods.md#9-security-validation) |
24| Anti-Patterns | Consolidated list from all skill areas | [anti-patterns.md](references/anti-patterns.md) |
25
26---
27
28## Decision Tree — Review Workflow
29
30```
31START: What are you reviewing?
32├─ Workflow JSON file (.json)
33│ ├─ Run: Workflow JSON checks (Section 1)
34│ ├─ Run: Connection Wiring checks (Section 2)
35│ ├─ Run: Expression checks on all parameter values (Section 3)
36│ ├─ Run: Error Handling checks (Section 6)
37│ └─ Run: Anti-Pattern scan (Section 10)
38│
39├─ Custom node code (.node.ts)
40│ ├─ Run: Node Type checks (Section 5)
41│ ├─ Run: Credential checks if node uses credentials (Section 4)
42│ ├─ Run: Error Handling checks (Section 6)
43│ └─ Run: Anti-Pattern scan (Section 10)
44│
45├─ Credential definition (.credentials.ts)
46│ └─ Run: Credential checks (Section 4)
47│
48├─ Code node content
49│ ├─ Run: Code Node checks (Section 8)
50│ └─ Run: Anti-Pattern scan (Section 10)
51│
52├─ Deployment config (docker-compose.yml / env vars)
53│ ├─ Run: Deployment checks (Section 7)
54│ └─ Run: Security checks (Section 9)
55│
56└─ Full project audit
57 └─ Run ALL sections sequentially
58```
59
60---
61
62## 1. Workflow JSON Validation
63
64ALWAYS verify these required fields on every node in `nodes[]`:
65
66| Field | Type | Rule |
67|-------|------|------|
68| `id` | string | MUST be unique UUID |
69| `name` | string | MUST be unique within the workflow |
70| `type` | string | MUST match a registered node type (e.g., `n8n-nodes-base.httpRequest`) |
71| `typeVersion` | number | MUST be a valid version for the node type |
72| `position` | [number, number] | MUST be `[x, y]` coordinate array |
73| `parameters` | object | MUST exist (can be empty `{}`) |
74
75ALWAYS verify the workflow root object contains:
76- `id` (string)
77- `name` (string)
78- `active` (boolean)
79- `nodes` (array)
80- `connections` (object)
81
82NEVER accept a workflow where two nodes share the same `name` — connections reference nodes by name, so duplicates break wiring.
83
84---
85
86## 2. Connection Wiring Validation
87
88IConnections uses **3-level nesting**:
89
90```
91connections[sourceNodeName][connectionType][outputIndex] = IConnection[]
92```
93
94ALWAYS verify:
951. Every key in `connections` matches a `name` in `nodes[]`
962. Every `IConnection.node` value matches a `name` in `nodes[]`
973. `IConnection.type` is a valid `NodeConnectionType` (usually `"main"`)
984. `IConnection.index` does not exceed the destination node's input count
995. Trigger nodes (`group: ['trigger']`) have NO incoming connections
1006. Non-trigger nodes have at least one incoming connection (unless intentionally orphaned)
1017. Multi-output nodes (IF, Switch) have the correct number of output arrays
102
103**IF node pattern**: `connections["IF"].main` MUST have exactly 2 arrays — index 0 for true, index 1 for false.
104
105---
106
107## 3. Expression Validation
108
109ALWAYS verify expressions (`{{ ... }}`) use correct variable references:
110
111| Context | Available | NOT Available |
112|---------|-----------|---------------|
113| Any expression | `$json`, `$binary`, `$input`, `$execution`, `$workflow`, `$now`, `$today`, `$env`, `$vars`, `$prevNode`, `$runIndex`, `$parameter` | — |
114| Code node | All `$` vars except `$itemIndex` and `$secrets` | `$itemIndex`, `$secrets` |
115| Python Code node | `_` prefix versions (`_json`, `_items`) | `$` prefix, dot notation on items |
116
117ALWAYS verify `$jmespath(object, searchString)` parameter order — object FIRST, search string SECOND. This differs from the JMESPath spec.
118
119NEVER allow `$("<NodeName>")` to reference a node name that does not exist in the workflow.
120
121---
122
123## 4. Credential Validation
124
125ALWAYS verify `ICredentialType` implementations include:
126
127| Property | Required | Rule |
128|----------|----------|------|
129| `name` | YES | Internal identifier, matches node's credential reference |
130| `displayName` | YES | Human-readable label |
131| `properties` | YES | Array of `INodeProperties[]` defining input fields |
132| `authenticate` | YES | Method with `type: 'generic'` and `properties` object |
133| `test` | RECOMMENDED | `ICredentialTestRequest` with test endpoint |
134
135ALWAYS verify `authenticate.type` is `'generic'` — other values are not supported.
136
137ALWAYS verify credential expressions use `$credentials` prefix: `={{$credentials.apiKey}}`.
138
139NEVER allow credentials to be hardcoded in node parameters — ALWAYS use credential references.
140
141---
142
143## 5. Node Type Validation
144
145ALWAYS verify `INodeType` implementations:
146
147| Check | Expected | Common Failure |
148|-------|----------|----------------|
149| `description` property | `INodeTypeDescription` with all required fields | Missing `inputs`, `outputs`, or `properties` |
150| `execute()` return type | `Promise<INodeExecutionData[][]>` | Returning single array `[]` instead of `[[]]` |
151| Trigger nodes | `inputs: []` and `group: ['trigger']` | Having inputs on trigger nodes |
152| Property `type` values | Valid `NodePropertyTypes` | Using invalid type strings |
153| `displayOptions` | References existing property names/values | Referencing non-existent parameters |
154| `credentials` array | Each entry has `name` matching a credential type | Credential name mismatch |
155
156ALWAYS verify `execute()` returns `[returnData]` (wrapped in outer array), NOT just `returnData`.
157
158ALWAYS verify each item in the return array has a `json` property: `{ json: { ... } }`.
159
160---
161
162## 6. Error Handling Validation
163
164ALWAYS verify:
165
1661. **Error workflow configured** — `settings.errorWorkflow` is set in production workflows
1672. **continueOnFail pattern** — nodes using `this.continueOnFail()` include error data in output:
168 ```typescript
169 returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
170 ```
1713. **Retry on transient failures** — HTTP/API nodes set `retryOnFail: true`, `maxTries >= 2`, `waitBetweenTries >= 1000`
1724. **Error node exists** — at least one Error Trigger workflow is available for the instance
1735. **`onError` setting** — nodes specify behavior: `'continueErrorOutput'`, `'continueRegularOutput'`, or `'stopWorkflow'`
174
175NEVER allow a production workflow without an error workflow — silent failures are unacceptable.
176
177---
178
179## 7. Deployment Validation
180
181ALWAYS verify for production deployments:
182
183| Check | Expected | Consequence of Missing |
184|-------|----------|----------------------|
185| `N8N_ENCRYPTION_KEY` | Explicitly set and backed up | Key regeneration locks out all credentials |
186| `NODE_ENV` | `production` | Missing production optimizations |
187| `N8N_PROTOCOL` | `https` | Credentials transmitted in cleartext |
188| `WEBHOOK_URL` | Set to public URL | Webhooks unreachable externally |
189| `N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS` | `true` | Settings file readable by other users |
190| `N8N_RUNNERS_ENABLED` | `true` | Code runs in main process (security risk) |
191| Volume: `/home/node/.n8n` | Mounted to persistent volume | Data lost on container restart |
192
193**Queue mode additional requirements**:
194
195| Check | Expected | Consequence of Missing |
196|-------|----------|----------------------|
197| `DB_TYPE` | `postgresdb` | SQLite does NOT support queue mode |
198| `EXECUTIONS_MODE` | `queue` | Workers will not process jobs |
199| Redis configured | `QUEUE_BULL_REDIS_HOST` + port | Queue has no broker |
200| Shared `N8N_ENCRYPTION_KEY` | Same key on main + all workers | Credential decryption fails |
201| S3 binary storage | Configured for shared access | Binary data inaccessible across instances |
202
203---
204
205## 8. Code Node Validation
206
207ALWAYS verify Code node content:
208
209| Check | Rule |
210|-------|------|
211| Return format (all items) | MUST return `[{json: {...}}, ...]` — array of objects with `json` key |
212| Return format (each item) | MUST return `{json: {...}}` — single object with `json` key |
213| No `$itemIndex` | NEVER use `$itemIndex` in Code node — it is not available |
214| No `$secrets` | NEVER use `$secrets` in Code node — it is not available |
215| No HTTP requests | NEVER make HTTP calls in Code node — use HTTP Request node |
216| No file system access | NEVER access files directly — use Read/Write Files nodes |
217| Python bracket notation | ALWAYS use `item["json"]["field"]`, NEVER `item.json.field` in Python |
218| Binary data access | ALWAYS use `this.helpers.getBinaryDataBuffer()`, NEVER direct buffer access |
219
220---
221
222## 9. Security Validation
223
224ALWAYS verify:
225
2261. **No hardcoded credentials** — API keys, tokens, passwords NEVER in node parameters or Code node
2272. **Encryption key set** — `N8N_ENCRYPTION_KEY` is explicitly configured (not auto-generated)
2283. **Task runners enabled** — `N8N_RUNNERS_ENABLED=true` (isolates Code node execution)
2294. **File access restricted** — `N8N_RESTRICT_FILE_ACCESS_TO` limits filesystem paths
2305. **Settings permissions** — `N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true`
2316. **Env access blocked** — `N8N_BLOCK_ENV_ACCESS_IN_NODE=true` if env vars contain secrets
2327. **Webhook authentication** — production webhooks use Basic Auth, Header Auth, or JWT
2338. **HTTPS enforced** — `N8N_PROTOCOL=https` with valid TLS termination
2349. **Secure cookies** — `N8N_SECURE_COOKIE=true` in HTTPS deployments
235
236---
237
238## 10. Anti-Pattern Detection
239
240Scan for ALL anti-patterns listed in [anti-patterns.md](references/anti-patterns.md). Key categories:
241
242- **Expression anti-patterns**: Wrong variable context, reversed JMESPath args, `new Date()` instead of Luxon
243- **Code node anti-patterns**: Restricted variables, wrong return format, direct binary access
244- **Credential anti-patterns**: Hardcoded secrets, missing test endpoint, wrong authenticate type
245- **Deployment anti-patterns**: Missing encryption key, SQLite in production, no volume mounts
246- **Workflow anti-patterns**: No error workflow, duplicate node names, orphan nodes
247
248---
249
250## Review Report Template
251
252After completing all applicable checks, produce a report:
253
254```markdown
255## n8n Review Report
256
257**Target**: [filename or description]
258**Type**: [Workflow JSON | Custom Node | Credential | Deployment Config | Code Node]
259**Date**: [date]
260
261### Summary
262- Total checks: [N]
263- Passed: [N]
264- Failed: [N]
265- Warnings: [N]
266
267### Critical Failures
2681. [Area] — [What failed] — [Expected state] — [How to fix]
269
270### Warnings
2711. [Area] — [What to improve] — [Recommendation]
272
273### Anti-Patterns Detected
2741. [AP-XXX] — [Description] — [Location in code/config]
275```
276
277---
278
279## Reference Links
280
281- [Validation Methods (Complete Checklist)](references/methods.md)
282- [Review Examples (Good/Bad/Fix)](references/examples.md)
283- [Anti-Pattern Catalog](references/anti-patterns.md)