n8n-agents-review
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
connectionsmatches anameinnodes[] - Every
IConnection.nodevalue matches anameinnodes[] IConnection.typeis a validNodeConnectionType(usually"main")IConnection.indexdoes 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.errorWorkflowis 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
onErrorsetting — 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_KEYis explicitly configured (not auto-generated) - Task runners enabled —
N8N_RUNNERS_ENABLED=true(isolates Code node execution) - File access restricted —
N8N_RESTRICT_FILE_ACCESS_TOlimits filesystem paths - Settings permissions —
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true - Env access blocked —
N8N_BLOCK_ENV_ACCESS_IN_NODE=trueif env vars contain secrets - Webhook authentication — production webhooks use Basic Auth, Header Auth, or JWT
- HTTPS enforced —
N8N_PROTOCOL=httpswith valid TLS termination - Secure cookies —
N8N_SECURE_COOKIE=truein 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]