
N8n Validation Expert
- 5k installs
- 6k repo stars
- Updated August 4, 2026
- czlonkowski/n8n-skills
n8n-validation-expert is an agent skill for interpreting n8n validation errors, warnings, and false positives during workflow fixes.
About
The n8n-validation-expert skill explains n8n validation feedback when validate_node or validate_workflow returns errors or warnings during workflow authoring. Validation is iterative: expect two to three validate-fix cycles averaging roughly twenty-three seconds thinking and fifty-eight seconds fixing per skill notes. Errors block activation and include missing_required fields, invalid_value options, type_mismatch types, and invalid_reference nodes. Warnings may include false positives the skill helps identify versus real fixes for operator structure and expression issues. Consult this skill whenever validation output appears to know which warnings can be ignored and which errors must be resolved before activation. Philosophy emphasizes validate early and often rather than one-shot perfection. Also covers validation profiles, error taxonomy, auto-fix capabilities where applicable, and guidance for nested node parameter failures in complex multi-branch workflows across large n8n workflow automation graphs.
- Treats validation as iterative with typical 2-3 validate-fix cycles before activation.
- Classifies blocking errors: missing_required, invalid_value, type_mismatch, invalid_reference.
- Helps separate false-positive warnings from operator structure issues needing fixes.
- Invoke whenever validate_node or validate_workflow returns errors or warnings.
- Documents average 23s error analysis and 58s fix time benchmarks from skill.
N8n Validation Expert by the numbers
- 5,023 all-time installs (skills.sh)
- +144 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #74 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
n8n-validation-expert capabilities & compatibility
- Capabilities
- validation error taxonomy interpretation · false positive warning detection · iterative validate fix workflow guidance · operator structure issue diagnosis · activation blocker resolution
- Works with
- n8n
- Use cases
- orchestration · debugging
What n8n-validation-expert says it does
Average: 23s thinking about errors, 58s fixing them
npx skills add https://github.com/czlonkowski/n8n-skills --skill n8n-validation-expertAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5k |
|---|---|
| repo stars | ★ 6k |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | czlonkowski/n8n-skills ↗ |
How do I fix n8n validate_workflow errors without chasing false-positive warnings?
Interpret n8n validate_node and validate_workflow errors, distinguish false positives, and guide iterative fixes across validation loops.
Who is it for?
Developers building n8n workflows who hit validate_node or validate_workflow failures.
Skip if: Skip when designing net-new workflows from scratch without validation output to interpret.
When should I use this skill?
validate_node or validate_workflow returns errors, warnings, or unclear validation profiles.
What you get
Resolved blocking validation errors with understood warning noise and corrected node parameters.
- Fixed n8n node configurations
- Validation error interpretation
- Filtered false-positive warning list
By the numbers
- Typical 2-3 validate-fix cycles expected
- Documented 23s think and 58s fix averages
Files
n8n Validation Expert
Expert guide for interpreting and fixing n8n validation errors.
---
Validation Philosophy
Validate early, validate often
Validation is typically iterative:
- Expect validation feedback loops
- Usually 2-3 validate → fix cycles
- Average: 23s thinking about errors, 58s fixing them
Key insight: Validation is an iterative process, not one-shot!
---
Error Severity Levels
1. Errors (Must Fix)
Blocks workflow execution - Must be resolved before activation
Types:
missing_required- Required field not providedinvalid_value- Value doesn't match allowed optionstype_mismatch- Wrong data type (string instead of number)invalid_reference- Referenced node doesn't existinvalid_expression- Expression syntax error
Example:
{
"type": "missing_required",
"property": "channel",
"message": "Channel name is required",
"fix": "Provide a channel name (lowercase, no spaces, 1-80 characters)"
}2. Warnings (Should Fix)
Doesn't block execution - Workflow can be activated but may have issues
Types:
best_practice- Recommended but not requireddeprecated- Using old API/featureperformance- Potential performance issue
Example:
{
"type": "best_practice",
"property": "errorHandling",
"message": "Slack API can have rate limits",
"suggestion": "Add onError: 'continueRegularOutput' with retryOnFail"
}3. Suggestions (Optional)
Nice to have - Improvements that could enhance workflow
Types:
optimization- Could be more efficientalternative- Better way to achieve same result
---
The Validation Loop
Pattern from Telemetry
7,841 occurrences of this pattern:
1. Configure node
↓
2. validate_node (23 seconds thinking about errors)
↓
3. Read error messages carefully
↓
4. Fix errors
↓
5. validate_node again (58 seconds fixing)
↓
6. Repeat until valid (usually 2-3 iterations)Example
// Iteration 1
let config = {
resource: "channel",
operation: "create"
};
const result1 = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// → Error: Missing "name"
// ⏱️ 23 seconds thinking...
// Iteration 2
config.name = "general";
const result2 = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// → Error: Missing "text"
// ⏱️ 58 seconds fixing...
// Iteration 3
config.text = "Hello!";
const result3 = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// → Valid! ✅This is normal! Don't be discouraged by multiple iterations.
---
Validation Profiles
Choose the right profile for your stage:
minimal
Use when: Quick checks during editing
Validates:
- Only required fields
- Basic structure
Pros: Fastest, most permissive Cons: May miss issues
runtime (RECOMMENDED)
Use when: Pre-deployment validation
Validates:
- Required fields
- Value types
- Allowed values
- Basic dependencies
Pros: Balanced, catches real errors Cons: Some edge cases missed
This is the recommended profile for most use cases
ai-friendly
Use when: AI-generated configurations
Validates:
- Same as runtime
- Reduces false positives
- More tolerant of minor issues
Pros: Less noisy for AI workflows Cons: May allow some questionable configs
strict
Use when: Production deployment, critical workflows
Validates:
- Everything
- Best practices
- Performance concerns
- Security issues
Pros: Maximum safety Cons: Many warnings, some false positives
---
Common Error Types
Five core error types, in rough order of frequency:
- `missing_required` — a required field isn't provided. Use
get_nodeto see required fields, then add it. - `invalid_value` — value doesn't match allowed options (enums are case-sensitive). Check the error's allowed list or
get_node. - `type_mismatch` — wrong data type (string
"100"vs number100). Convert to the expected type. - `invalid_expression` — expression syntax error (missing
{{}}, typos). See the n8n Expression Syntax skill. - `invalid_reference` — referenced node doesn't exist (renamed, deleted, or misspelled). Fix the name or
cleanStaleConnections.
A sixth class, `patchNodeField` errors (find-not-found, ambiguous match, invalid/unsafe regex), surfaces when a patchNodeField op fails during n8n_update_partial_workflow — it's strict by design and errors rather than silently continuing.
Every type above has worked examples (broken config → fix) plus the patchNodeField error cases and their fixes in [ERROR_CATALOG.md](ERROR_CATALOG.md).
---
Auto-Sanitization System
Automatically fixes common operator structure issues on ANY workflow update — n8n_create_workflow, n8n_update_partial_workflow, or any save. Trust it; don't hand-fix these.
What it fixes:
- Binary operators (equals, notEquals, contains, notContains, greaterThan, lessThan, startsWith, endsWith) — removes the wrong
singleValueproperty. - Unary operators (isEmpty, isNotEmpty, true, false) — adds
singleValue: true. - IF/Switch metadata — adds complete
conditions.optionsmetadata for IF v2.2+ and Switch v3.2+.
What it CANNOT fix (handle manually): broken connections to non-existent nodes (use cleanStaleConnections), branch-count mismatches (add/remove connections or rules), and paradoxical corrupt states (may need manual DB intervention).
Before/after examples and the full cannot-fix detail are in [ERROR_CATALOG.md](ERROR_CATALOG.md) (Auto-Sanitization sections).
---
False Positives
Validation warnings that are technically "wrong" but acceptable in your use case. Not every warning needs a fix — many are context-dependent. Common ones and when each is acceptable vs. worth fixing:
- "Missing error handling" — OK for dev/testing and non-critical notifications; fix for production handling important data.
- "No retry logic" — OK for idempotent ops, APIs with their own retry, manual triggers; fix for flaky external services and production automation.
- "Missing rate limiting" — OK for internal/low-volume/server-side-limited APIs; fix for public, high-volume APIs.
- "Unbounded query" — OK for small known datasets, aggregations, dev/testing; fix for production queries on large tables.
Reduce false positives with the ai-friendly profile (e.g. validate_node({nodeType, config, profile: "ai-friendly"})).
Full per-case guidance, security/credential warnings, known n8n false-positive issues (#304, #306, #338), profile strategies, the "should I fix this?" decision framework, and how to document accepted warnings are in [FALSE_POSITIVES.md](FALSE_POSITIVES.md).
---
Validation Result Structure
Complete Response
{
"valid": false,
"errors": [
{
"type": "missing_required",
"property": "channel",
"message": "Channel name is required",
"fix": "Provide a channel name (lowercase, no spaces)"
}
],
"warnings": [
{
"type": "best_practice",
"property": "errorHandling",
"message": "Slack API can have rate limits",
"suggestion": "Add onError: 'continueRegularOutput'"
}
],
"suggestions": [
{
"type": "optimization",
"message": "Consider using batch operations for multiple messages"
}
],
"summary": {
"hasErrors": true,
"errorCount": 1,
"warningCount": 1,
"suggestionCount": 1
}
}How to Read It
1. Check `valid` first — true means the config is valid; false means there are errors to fix before deployment. 2. Fix `errors` first — each carries a property, message, and fix. These must be resolved. 3. Review `warnings` — each has a message and suggestion; decide per-case whether to address it (see False Positives above). 4. Consider `suggestions` — optional improvements, not required.
---
Workflow Validation
validate_workflow (Structure)
Validates entire workflow, not just individual nodes
Checks: 1. Node configurations - Each node valid 2. Connections - No broken references 3. Expressions - Syntax and references valid 4. Flow - Logical workflow structure
Example:
validate_workflow({
workflow: {
nodes: [...],
connections: {...}
},
options: {
validateNodes: true,
validateConnections: true,
validateExpressions: true,
profile: "runtime"
}
})Common Workflow Errors
1. Broken Connections
{
"error": "Connection from 'Transform' to 'NonExistent' - target node not found"
}Fix: Remove stale connection or create missing node
2. Circular Dependencies
{
"error": "Circular dependency detected: Node A → Node B → Node A"
}Fix: Restructure workflow to remove loop
3. Multiple Start Nodes
{
"warning": "Multiple trigger nodes found - only one will execute"
}Fix: Remove extra triggers or split into separate workflows
4. Disconnected Nodes
{
"warning": "Node 'Transform' is not connected to workflow flow"
}Fix: Connect node or remove if unused
---
Recovery Strategies
Strategy 1: Start Fresh
When: Configuration is severely broken
Steps: 1. Note required fields from get_node 2. Create minimal valid configuration 3. Add features incrementally 4. Validate after each addition
Strategy 2: Binary Search
When: Workflow validates but executes incorrectly
Steps: 1. Remove half the nodes 2. Validate and test 3. If works: problem is in removed nodes 4. If fails: problem is in remaining nodes 5. Repeat until problem isolated
Strategy 3: Clean Stale Connections
When: "Node not found" errors
Steps:
n8n_update_partial_workflow({
id: "workflow-id",
operations: [{
type: "cleanStaleConnections"
}]
})Strategy 4: Use Auto-fix
When: Validation errors that can be automatically resolved
Steps:
// Preview fixes (default - doesn't apply)
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: false,
confidenceThreshold: "medium" // high, medium, low
})
// Review fixes, then apply
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: true
})---
Auto-Fix Capabilities
The n8n_autofix_workflow tool can fix these issue types:
1. expression-format - Missing = prefix in expressions (e.g., {{ $json.field }} → ={{ $json.field }}) 2. typeversion-correction - Downgrades nodes with unsupported typeVersions 3. error-output-config - Removes conflicting onError settings 4. node-type-correction - Fixes unknown node types using similarity matching (90%+ confidence) 5. webhook-missing-path - Generates UUIDs for webhook nodes missing path configuration 6. typeversion-upgrade - Smart upgrades to latest node versions with auto-migration 7. version-migration - Guidance for complex breaking changes requiring manual steps
Confidence levels: high (90%+, safe to auto-apply), medium (70-89%, review recommended), low (<70%, manual review required)
// Preview all fixes
n8n_autofix_workflow({id: "workflow-id"})
// Only apply high-confidence fixes
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: true,
confidenceThreshold: "high"
})
// Target specific fix types
n8n_autofix_workflow({
id: "workflow-id",
fixTypes: ["expression-format", "typeversion-upgrade"],
applyFixes: true
})Post-update guidance: For version upgrades, check the postUpdateGuidance field in the response for step-by-step migration instructions.
---
Best Practices
✅ Do
- Validate after every significant change
- Read error messages completely
- Fix errors iteratively (one at a time)
- Use
runtimeprofile for pre-deployment - Check
validfield before assuming success - Trust auto-sanitization for operator issues
- Use
get_nodewhen unclear about requirements - Document false positives you accept
❌ Don't
- Skip validation before activation
- Try to fix all errors at once
- Ignore error messages
- Use
strictprofile during development (too noisy) - Assume validation passed (always check result)
- Manually fix auto-sanitization issues
- Deploy with unresolved errors
- Ignore all warnings (some are important!)
---
Reviewing an existing workflow
Validating as you build (the loop above) is for catching schema and shape errors in your own in-progress work. Reviewing an existing workflow — yours or one you've been handed — is a different job: the workflow already passes validate_workflow clean, and you're hunting for the issues validation doesn't see (silent connection bugs, injection-prone queries, dropped-item Switches, Set/Code antipatterns, missing error paths). For that, pull the workflow with n8n_get_workflow and walk [REVIEW_CHECKLIST.md](REVIEW_CHECKLIST.md) — a severity-tiered audit (MUST FIX / SHOULD FIX / NICE TO HAVE) where every item points to the canonical skill for the fix. Run n8n_audit_instance alongside it to surface hardcoded secrets and unauthenticated webhooks across the whole instance.
---
Detailed Guides
For comprehensive error catalogs, false positives, and workflow review:
- [ERROR_CATALOG.md](ERROR_CATALOG.md) - Complete list of error types with examples
- [FALSE_POSITIVES.md](FALSE_POSITIVES.md) - When warnings are acceptable
- [REVIEW_CHECKLIST.md](REVIEW_CHECKLIST.md) - Severity-tiered audit for reviewing an existing workflow
---
Summary
Key Points: 1. Validation is iterative (avg 2-3 cycles, 23s + 58s) 2. Errors must be fixed, warnings are optional 3. Auto-sanitization fixes operator structures automatically 4. Use runtime profile for balanced validation 5. False positives exist - learn to recognize them 6. Read error messages - they contain fix guidance
Validation Process: 1. Validate → Read errors → Fix → Validate again 2. Repeat until valid (usually 2-3 iterations) 3. Review warnings and decide if acceptable 4. Deploy with confidence
Related Skills & Tools:
- n8n MCP Tools Expert - Use validation tools correctly
- n8n Expression Syntax - Fix expression errors
- n8n Node Configuration - Understand required fields
n8n_audit_instance- Proactive security validation (hardcoded secrets, unauthenticated webhooks, missing error handling, data retention)
Error Catalog
Comprehensive catalog of n8n validation errors with real examples and fixes.
---
Error Types Overview
Common validation errors by priority:
| Error Type | Priority | Severity | Auto-Fix |
|---|---|---|---|
| missing_required | Highest | Error | ❌ |
| invalid_value | High | Error | ❌ |
| type_mismatch | Medium | Error | ❌ |
| invalid_expression | Medium | Error | ❌ |
| invalid_reference | Low | Error | ❌ |
| operator_structure | Lowest | Warning | ✅ |
---
Errors (Must Fix)
1. missing_required
What it means: Required field is not provided in node configuration
When it occurs:
- Creating new nodes without all required fields
- Copying configurations between different operations
- Switching operations that have different requirements
Most common validation error
Example 1: Slack Channel Missing
Error:
{
"type": "missing_required",
"property": "channel",
"message": "Channel name is required",
"node": "Slack",
"path": "parameters.channel"
}Broken Configuration:
{
"resource": "message",
"operation": "post"
// Missing: channel
}Fix:
{
"resource": "message",
"operation": "post",
"channel": "#general" // ✅ Added required field
}How to identify required fields:
// Use get_node to see what's required
const info = get_node({
nodeType: "nodes-base.slack"
});
// Check properties marked as "required": trueExample 2: HTTP Request Missing URL
Error:
{
"type": "missing_required",
"property": "url",
"message": "URL is required for HTTP Request",
"node": "HTTP Request",
"path": "parameters.url"
}Broken Configuration:
{
"method": "GET",
"authentication": "none"
// Missing: url
}Fix:
{
"method": "GET",
"authentication": "none",
"url": "https://api.example.com/data" // ✅ Added
}Example 3: Database Query Missing Connection
Error:
{
"type": "missing_required",
"property": "query",
"message": "SQL query is required",
"node": "Postgres",
"path": "parameters.query"
}Broken Configuration:
{
"operation": "executeQuery"
// Missing: query
}Fix:
{
"operation": "executeQuery",
"query": "SELECT * FROM users WHERE active = true" // ✅ Added
}Example 4: Conditional Fields
Error:
{
"type": "missing_required",
"property": "body",
"message": "Request body is required when sendBody is true",
"node": "HTTP Request",
"path": "parameters.body"
}Broken Configuration:
{
"method": "POST",
"url": "https://api.example.com/create",
"sendBody": true
// Missing: body (required when sendBody=true)
}Fix:
{
"method": "POST",
"url": "https://api.example.com/create",
"sendBody": true,
"body": {
"contentType": "json",
"content": {
"name": "John",
"email": "john@example.com"
}
} // ✅ Added conditional required field
}---
2. invalid_value
What it means: Provided value doesn't match allowed options or format
When it occurs:
- Using wrong enum value
- Typos in operation names
- Invalid format for specialized fields (emails, URLs, channels)
Second most common error
Example 1: Invalid Operation
Error:
{
"type": "invalid_value",
"property": "operation",
"message": "Operation must be one of: post, update, delete, get",
"current": "send",
"allowed": ["post", "update", "delete", "get"]
}Broken Configuration:
{
"resource": "message",
"operation": "send" // ❌ Invalid - should be "post"
}Fix:
{
"resource": "message",
"operation": "post" // ✅ Use valid operation
}Example 2: Invalid HTTP Method
Error:
{
"type": "invalid_value",
"property": "method",
"message": "Method must be one of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS",
"current": "FETCH",
"allowed": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
}Broken Configuration:
{
"method": "FETCH", // ❌ Invalid
"url": "https://api.example.com"
}Fix:
{
"method": "GET", // ✅ Use valid HTTP method
"url": "https://api.example.com"
}Example 3: Invalid Channel Format
Error:
{
"type": "invalid_value",
"property": "channel",
"message": "Channel name must start with # and be lowercase (e.g., #general)",
"current": "General"
}Broken Configuration:
{
"resource": "message",
"operation": "post",
"channel": "General" // ❌ Wrong format
}Fix:
{
"resource": "message",
"operation": "post",
"channel": "#general" // ✅ Correct format
}Example 4: Invalid Enum with Case Sensitivity
Error:
{
"type": "invalid_value",
"property": "resource",
"message": "Resource must be one of: channel, message, user, file",
"current": "Message",
"allowed": ["channel", "message", "user", "file"]
}Note: Enums are case-sensitive!
Broken Configuration:
{
"resource": "Message", // ❌ Capital M
"operation": "post"
}Fix:
{
"resource": "message", // ✅ Lowercase
"operation": "post"
}---
3. type_mismatch
What it means: Value is wrong data type (string instead of number, etc.)
When it occurs:
- Hardcoding values that should be numbers
- Using expressions where literals are expected
- JSON serialization issues
Common error
Example 1: String Instead of Number
Error:
{
"type": "type_mismatch",
"property": "limit",
"message": "Expected number, got string",
"expected": "number",
"current": "100"
}Broken Configuration:
{
"operation": "executeQuery",
"query": "SELECT * FROM users",
"limit": "100" // ❌ String
}Fix:
{
"operation": "executeQuery",
"query": "SELECT * FROM users",
"limit": 100 // ✅ Number
}Example 2: Number Instead of String
Error:
{
"type": "type_mismatch",
"property": "channel",
"message": "Expected string, got number",
"expected": "string",
"current": 12345
}Broken Configuration:
{
"resource": "message",
"operation": "post",
"channel": 12345 // ❌ Number (even if channel ID)
}Fix:
{
"resource": "message",
"operation": "post",
"channel": "#general" // ✅ String (channel name, not ID)
}Example 3: Boolean as String
Error:
{
"type": "type_mismatch",
"property": "sendHeaders",
"message": "Expected boolean, got string",
"expected": "boolean",
"current": "true"
}Broken Configuration:
{
"method": "GET",
"url": "https://api.example.com",
"sendHeaders": "true" // ❌ String "true"
}Fix:
{
"method": "GET",
"url": "https://api.example.com",
"sendHeaders": true // ✅ Boolean true
}Example 4: Object Instead of Array
Error:
{
"type": "type_mismatch",
"property": "tags",
"message": "Expected array, got object",
"expected": "array",
"current": {"tag": "important"}
}Broken Configuration:
{
"name": "New Channel",
"tags": {"tag": "important"} // ❌ Object
}Fix:
{
"name": "New Channel",
"tags": ["important", "alerts"] // ✅ Array
}---
4. invalid_expression
What it means: n8n expression has syntax errors or invalid references
When it occurs:
- Missing
{{}}around expressions - Typos in variable names
- Referencing non-existent nodes or fields
- Invalid JavaScript syntax in expressions
Moderately common
Related: See n8n Expression Syntax skill for comprehensive expression guidance
Example 1: Missing Curly Braces
Error:
{
"type": "invalid_expression",
"property": "text",
"message": "Expressions must be wrapped in {{}}",
"current": "$json.name"
}Broken Configuration:
{
"resource": "message",
"operation": "post",
"channel": "#general",
"text": "$json.name" // ❌ Missing {{}}
}Fix:
{
"resource": "message",
"operation": "post",
"channel": "#general",
"text": "={{$json.name}}" // ✅ Wrapped in {{}}
}Example 2: Invalid Node Reference
Error:
{
"type": "invalid_expression",
"property": "value",
"message": "Referenced node 'HTTP Requets' does not exist",
"current": "={{$node['HTTP Requets'].json.data}}"
}Broken Configuration:
{
"field": "data",
"value": "={{$node['HTTP Requets'].json.data}}" // ❌ Typo in node name
}Fix:
{
"field": "data",
"value": "={{$node['HTTP Request'].json.data}}" // ✅ Correct node name
}Example 3: Invalid Property Access
Error:
{
"type": "invalid_expression",
"property": "text",
"message": "Cannot access property 'user' of undefined",
"current": "={{$json.data.user.name}}"
}Broken Configuration:
{
"text": "={{$json.data.user.name}}" // ❌ Structure doesn't exist
}Fix (with safe navigation):
{
"text": "={{$json.data?.user?.name || 'Unknown'}}" // ✅ Safe navigation + fallback
}Example 4: Webhook Data Access Error
Error:
{
"type": "invalid_expression",
"property": "value",
"message": "Property 'email' not found in $json",
"current": "={{$json.email}}"
}Common Gotcha: Webhook data is under .body!
Broken Configuration:
{
"field": "email",
"value": "={{$json.email}}" // ❌ Missing .body
}Fix:
{
"field": "email",
"value": "={{$json.body.email}}" // ✅ Webhook data under .body
}---
5. invalid_reference
What it means: Configuration references a node that doesn't exist in the workflow
When it occurs:
- Node was renamed or deleted
- Typo in node name
- Copy-pasting from another workflow
Less common error
Example 1: Deleted Node Reference
Error:
{
"type": "invalid_reference",
"property": "expression",
"message": "Node 'Transform Data' does not exist in workflow",
"referenced_node": "Transform Data"
}Broken Configuration:
{
"value": "={{$node['Transform Data'].json.result}}" // ❌ Node deleted
}Fix:
// Option 1: Update to existing node
{
"value": "={{$node['Set'].json.result}}"
}
// Option 2: Remove expression if not needed
{
"value": "default_value"
}Example 2: Connection to Non-Existent Node
Error:
{
"type": "invalid_reference",
"message": "Connection references node 'Slack1' which does not exist",
"source": "HTTP Request",
"target": "Slack1"
}Fix: Use cleanStaleConnections operation:
n8n_update_partial_workflow({
id: "workflow-id",
operations: [{
type: "cleanStaleConnections"
}]
})Example 3: Renamed Node Not Updated
Error:
{
"type": "invalid_reference",
"property": "expression",
"message": "Node 'Get Weather' does not exist (did you mean 'Weather API'?)",
"referenced_node": "Get Weather",
"suggestions": ["Weather API"]
}Broken Configuration:
{
"value": "={{$node['Get Weather'].json.temperature}}" // ❌ Old name
}Fix:
{
"value": "={{$node['Weather API'].json.temperature}}" // ✅ Current name
}---
Warnings (Should Fix)
6. best_practice
What it means: Configuration works but doesn't follow best practices
Severity: Warning (doesn't block execution)
When acceptable: Development, testing, simple workflows
When to fix: Production workflows, critical operations
Example 1: Missing Error Handling
Warning:
{
"type": "best_practice",
"property": "onError",
"message": "Slack API can have rate limits and connection issues",
"suggestion": "Add error handling: onError: 'continueRegularOutput'"
}Current Configuration:
{
"resource": "message",
"operation": "post",
"channel": "#alerts"
// No error handling ⚠️
}Recommended Fix:
{
"resource": "message",
"operation": "post",
"channel": "#alerts",
"continueOnFail": true,
"retryOnFail": true,
"maxTries": 3
}Example 2: No Retry Logic
Warning:
{
"type": "best_practice",
"property": "retryOnFail",
"message": "External API calls should retry on failure",
"suggestion": "Add retryOnFail: true, maxTries: 3, waitBetweenTries: 1000"
}When to ignore: Idempotent operations, APIs with their own retry logic
When to fix: Flaky external services, production automation
---
7. deprecated
What it means: Using old API version or deprecated feature
Severity: Warning (still works but may stop working in future)
When to fix: Always (eventually)
Example 1: Old typeVersion
Warning:
{
"type": "deprecated",
"property": "typeVersion",
"message": "typeVersion 1 is deprecated for Slack node, use version 2",
"current": 1,
"recommended": 2
}Fix:
{
"type": "n8n-nodes-base.slack",
"typeVersion": 2, // ✅ Updated
// May need to update configuration for new version
}---
8. performance
What it means: Configuration may cause performance issues
Severity: Warning
When to fix: High-volume workflows, large datasets
Example 1: Unbounded Query
Warning:
{
"type": "performance",
"property": "query",
"message": "SELECT without LIMIT can return massive datasets",
"suggestion": "Add LIMIT clause or use pagination"
}Current:
SELECT * FROM users WHERE active = trueFix:
SELECT * FROM users WHERE active = true LIMIT 1000---
Auto-Sanitization Fixes
9. operator_structure
What it means: IF/Switch operator structure issues
Severity: Warning
Auto-Fix: ✅ YES - Fixed automatically on workflow save
Rare (mostly auto-fixed)
Fixed Automatically: Binary Operators
Before (you create this):
{
"type": "boolean",
"operation": "equals",
"singleValue": true // ❌ Wrong for binary operator
}After (auto-sanitization fixes it):
{
"type": "boolean",
"operation": "equals"
// singleValue removed ✅
}You don't need to do anything - this is fixed on save!
Fixed Automatically: Unary Operators
Before:
{
"type": "boolean",
"operation": "isEmpty"
// Missing singleValue ❌
}After:
{
"type": "boolean",
"operation": "isEmpty",
"singleValue": true // ✅ Added automatically
}What you should do: Trust auto-sanitization, don't manually fix these!
---
patchNodeField Errors
What it means: A patchNodeField operation failed during n8n_update_partial_workflow
The patchNodeField operation is strict by design — it errors instead of silently continuing when something is wrong. This catches mistakes early but means you need to handle these specific error cases.
Error: Find string not found
The patch's find value doesn't exist in the target field. This usually means the content was already changed, or the find string has a typo.
patchNodeField: find string not found in field "parameters.jsCode"How to fix: Double-check the exact string. Use n8n_get_workflow to inspect the current field value. Whitespace and line endings matter — if unsure, use regex: true with \s+ for flexible whitespace matching.
Error: Ambiguous match (multiple occurrences)
The find string appears more than once in the field. Without replaceAll: true, this is treated as ambiguous and rejected.
patchNodeField: find string matches 3 times in field "parameters.jsCode" — set replaceAll: true to replace all, or use a more specific find stringHow to fix: Either set replaceAll: true if you want to replace all occurrences, or make your find string more specific to match only the intended location.
Error: Invalid regex pattern
When regex: true, the pattern is validated for correctness and safety.
patchNodeField: invalid or unsafe regex patternHow to fix: Check regex syntax. Nested quantifiers like (a+)+ and overlapping alternations like (\w|\d)+ are rejected as ReDoS risks. Simplify the pattern.
---
Auto-Sanitization: What It CANNOT Fix
Auto-sanitization handles operator structure (binary/unary singleValue, IF/Switch metadata) automatically on every save. It does not fix these — you must handle them manually:
Broken Connections
References to non-existent nodes.
Solution: Use the cleanStaleConnections operation in n8n_update_partial_workflow.
Branch Count Mismatches
3 Switch rules but only 2 output connections.
Solution: Add missing connections or remove extra rules.
Paradoxical Corrupt States
API returns corrupt data but rejects updates.
Solution: May require manual database intervention.
---
Recovery Patterns
Pattern 1: Progressive Validation
Problem: Too many errors at once
Solution:
// Step 1: Minimal valid config
let config = {
resource: "message",
operation: "post",
channel: "#general",
text: "Hello"
};
validate_node({nodeType: "nodes-base.slack", config, profile: "runtime"});
// ✅ Valid
// Step 2: Add features one by one
config.attachments = [...];
validate_node({nodeType: "nodes-base.slack", config, profile: "runtime"});
config.blocks = [...];
validate_node({nodeType: "nodes-base.slack", config, profile: "runtime"});Pattern 2: Error Triage
Problem: Multiple errors
Solution:
const result = validate_node({...});
// 1. Fix errors (must fix)
result.errors.forEach(error => {
console.log(`MUST FIX: ${error.property} - ${error.message}`);
});
// 2. Review warnings (should fix)
result.warnings.forEach(warning => {
console.log(`SHOULD FIX: ${warning.property} - ${warning.message}`);
});
// 3. Consider suggestions (optional)
result.suggestions.forEach(sug => {
console.log(`OPTIONAL: ${sug.message}`);
});Pattern 3: Use get_node
Problem: Don't know what's required
Solution:
// Before configuring, check requirements
const info = get_node({
nodeType: "nodes-base.slack"
});
// Look for required fields
info.properties.forEach(prop => {
if (prop.required) {
console.log(`Required: ${prop.name} (${prop.type})`);
}
});---
Summary
Most Common Errors: 1. missing_required (45%) - Always check get_node 2. invalid_value (28%) - Check allowed values 3. type_mismatch (12%) - Use correct data types 4. invalid_expression (8%) - Use Expression Syntax skill 5. invalid_reference (5%) - Clean stale connections
Auto-Fixed:
operator_structure- Trust auto-sanitization!
Related Skills:
- [SKILL.md](SKILL.md) - Main validation guide
- [FALSE_POSITIVES.md](FALSE_POSITIVES.md) - When to ignore warnings
- n8n Expression Syntax - Fix expression errors
- n8n MCP Tools Expert - Use validation tools correctly
False Positives Guide
When validation warnings are acceptable and how to handle them.
---
What Are False Positives?
Definition: Validation warnings that are technically "issues" but acceptable in your specific use case.
Key insight: Not all warnings need to be fixed!
Many warnings are context-dependent:
- ~40% of warnings are acceptable in specific use cases
- Using
ai-friendlyprofile reduces false positives by 60%
---
Philosophy
✅ Good Practice
1. Run validation with 'runtime' profile
2. Fix all ERRORS
3. Review each WARNING
4. Decide if acceptable for your use case
5. Document why you accepted it
6. Deploy with confidence❌ Bad Practice
1. Ignore all warnings blindly
2. Use 'minimal' profile to avoid warnings
3. Deploy without understanding risks---
Common False Positives
1. Missing Error Handling
Warning:
{
"type": "best_practice",
"message": "No error handling configured",
"suggestion": "Add continueOnFail: true and retryOnFail: true"
}When Acceptable
✅ Development/Testing Workflows
// Testing workflow - failures are obvious
{
"name": "Test Slack Integration",
"nodes": [{
"type": "n8n-nodes-base.slack",
"parameters": {
"resource": "message",
"operation": "post",
"channel": "#test"
// No error handling - OK for testing
}
}]
}Reasoning: You WANT to see failures during testing.
✅ Non-Critical Notifications
// Nice-to-have notification
{
"name": "Optional Slack Notification",
"parameters": {
"channel": "#general",
"text": "FYI: Process completed"
// If this fails, no big deal
}
}Reasoning: Notification failure doesn't affect core functionality.
✅ Manual Trigger Workflows
// Manual workflow - user is watching
{
"nodes": [{
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "manual-test"
// No error handling - user will retry manually
}
}]
}Reasoning: User is present to see and handle errors.
When to Fix
❌ Production Automation
// BAD: Critical workflow without error handling
{
"name": "Process Customer Orders",
"nodes": [{
"type": "n8n-nodes-base.postgres",
"parameters": {
"query": "INSERT INTO orders..."
// ❌ Should have error handling!
}
}]
}Fix:
{
"parameters": {
"query": "INSERT INTO orders...",
"continueOnFail": true,
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 1000
}
}❌ Critical Integrations
// BAD: Payment processing without error handling
{
"name": "Process Payment",
"type": "n8n-nodes-base.stripe"
// ❌ Payment failures MUST be handled!
}---
2. No Retry Logic
Warning:
{
"type": "best_practice",
"message": "External API calls should retry on failure",
"suggestion": "Add retryOnFail: true with exponential backoff"
}When Acceptable
✅ APIs with Built-in Retry
// Stripe has its own retry mechanism
{
"type": "n8n-nodes-base.stripe",
"parameters": {
"resource": "charge",
"operation": "create"
// Stripe SDK retries automatically
}
}✅ Idempotent Operations
// GET request - safe to retry manually if needed
{
"method": "GET",
"url": "https://api.example.com/status"
// Read-only, no side effects
}✅ Local/Internal Services
// Internal API with high reliability
{
"url": "http://localhost:3000/process"
// Local service, failures are rare and obvious
}When to Fix
❌ Flaky External APIs
// BAD: Known unreliable API without retries
{
"url": "https://unreliable-api.com/data"
// ❌ Should retry!
}
// GOOD:
{
"url": "https://unreliable-api.com/data",
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000
}❌ Non-Idempotent Operations
// BAD: POST without retry - may lose data
{
"method": "POST",
"url": "https://api.example.com/create"
// ❌ Could timeout and lose data
}---
3. Missing Rate Limiting
Warning:
{
"type": "best_practice",
"message": "API may have rate limits",
"suggestion": "Add rate limiting or batch requests"
}When Acceptable
✅ Internal APIs
// Internal microservice - no rate limits
{
"url": "http://internal-api/process"
// Company controls both ends
}✅ Low-Volume Workflows
// Runs once per day
{
"trigger": {
"type": "n8n-nodes-base.cron",
"parameters": {
"mode": "everyDay",
"hour": 9
}
},
"nodes": [{
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.example.com/daily-report"
// Once per day = no rate limit concerns
}
}]
}✅ APIs with Server-Side Limits
// API returns 429 and n8n handles it
{
"url": "https://api.example.com/data",
"options": {
"response": {
"response": {
"neverError": false // Will error on 429
}
}
},
"retryOnFail": true // Retry on 429
}When to Fix
❌ High-Volume Public APIs
// BAD: Loop hitting rate-limited API
{
"nodes": [{
"type": "n8n-nodes-base.splitInBatches",
"parameters": {
"batchSize": 100
}
}, {
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.github.com/..."
// ❌ GitHub has strict rate limits!
}
}]
}
// GOOD: Add rate limiting
{
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://api.github.com/...",
"options": {
"batching": {
"batch": {
"batchSize": 10,
"batchInterval": 1000 // 1 second between batches
}
}
}
}
}---
4. Unbounded Database Queries
Warning:
{
"type": "performance",
"message": "SELECT without LIMIT can return massive datasets",
"suggestion": "Add LIMIT clause or use pagination"
}When Acceptable
✅ Small Known Datasets
// Config table with ~10 rows
{
"query": "SELECT * FROM app_config"
// Known to be small, no LIMIT needed
}✅ Aggregation Queries
// COUNT/SUM operations
{
"query": "SELECT COUNT(*) as total FROM users WHERE active = true"
// Aggregation, not returning rows
}✅ Development/Testing
// Testing with small dataset
{
"query": "SELECT * FROM test_users"
// Test database has 5 rows
}When to Fix
❌ Production Queries on Large Tables
// BAD: User table could have millions of rows
{
"query": "SELECT * FROM users"
// ❌ Could return millions of rows!
}
// GOOD: Add LIMIT
{
"query": "SELECT * FROM users LIMIT 1000"
}
// BETTER: Use pagination
{
"query": "SELECT * FROM users WHERE id > {{$json.lastId}} LIMIT 1000"
}---
5. Missing Input Validation
Warning:
{
"type": "best_practice",
"message": "Webhook doesn't validate input data",
"suggestion": "Add IF node to validate required fields"
}When Acceptable
✅ Internal Webhooks
// Webhook from your own backend
{
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "internal-trigger"
// Your backend already validates
}
}✅ Trusted Sources
// Webhook from Stripe (cryptographically signed)
{
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "stripe-webhook",
"authentication": "headerAuth"
// Stripe signature validates authenticity
}
}When to Fix
❌ Public Webhooks
// BAD: Public webhook without validation
{
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "public-form-submit"
// ❌ Anyone can send anything!
}
}
// GOOD: Add validation
{
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook"
},
{
"name": "Validate Input",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{$json.body.email}}",
"operation": "isNotEmpty"
},
{
"value1": "={{$json.body.email}}",
"operation": "regex",
"value2": "^[^@]+@[^@]+\\.[^@]+$"
}
]
}
}
}
]
}---
6. Hardcoded Credentials
Warning:
{
"type": "security",
"message": "Credentials should not be hardcoded",
"suggestion": "Use n8n credential system"
}When Acceptable
✅ Public APIs (No Auth)
// Truly public API with no secrets
{
"url": "https://api.ipify.org"
// No credentials needed
}✅ Demo/Example Workflows
// Example workflow in documentation
{
"url": "https://example.com/api",
"headers": {
"Authorization": "Bearer DEMO_TOKEN"
}
// Clearly marked as example
}When to Fix (Always!)
❌ Real Credentials
// BAD: Real API key in workflow
{
"headers": {
"Authorization": "Bearer sk_live_abc123..."
}
// ❌ NEVER hardcode real credentials!
}
// GOOD: Use credentials system
{
"authentication": "headerAuth",
"credentials": {
"headerAuth": {
"id": "credential-id",
"name": "My API Key"
}
}
}---
Validation Profile Strategies
Strategy 1: Progressive Strictness
Development:
validate_node({
nodeType: "nodes-base.slack",
config,
profile: "ai-friendly" // Fewer warnings during development
})Pre-Production:
validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime" // Balanced validation
})Production Deployment:
validate_node({
nodeType: "nodes-base.slack",
config,
profile: "strict" // All warnings, review each one
})Strategy 2: Profile by Workflow Type
Quick Automations:
- Profile:
ai-friendly - Accept: Most warnings
- Fix: Only errors + security warnings
Business-Critical Workflows:
- Profile:
strict - Accept: Very few warnings
- Fix: Everything possible
Integration Testing:
- Profile:
minimal - Accept: All warnings (just testing connections)
- Fix: Only errors that prevent execution
---
Decision Framework
Should I Fix This Warning?
┌─────────────────────────────────┐
│ Is it a SECURITY warning? │
├─────────────────────────────────┤
│ YES → Always fix │
│ NO → Continue │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Is this a production workflow? │
├─────────────────────────────────┤
│ YES → Continue │
│ NO → Probably acceptable │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Does it handle critical data? │
├─────────────────────────────────┤
│ YES → Fix the warning │
│ NO → Continue │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Is there a known workaround? │
├─────────────────────────────────┤
│ YES → Acceptable if documented │
│ NO → Fix the warning │
└─────────────────────────────────┘---
Documentation Template
When accepting a warning, document why:
// workflows/customer-notifications.json
{
"nodes": [{
"name": "Send Slack Notification",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#notifications"
// ACCEPTED WARNING: No error handling
// Reason: Non-critical notification, failures are acceptable
// Reviewed: 2025-10-20
// Reviewer: Engineering Team
}
}]
}---
Known n8n Issues
Issue #304: IF Node Metadata Warning
Warning:
{
"type": "metadata_incomplete",
"message": "IF node missing conditions.options metadata",
"node": "IF"
}Status: False positive for IF v2.2+
Why it occurs: Auto-sanitization adds metadata, but validation runs before sanitization
What to do: Ignore - metadata is added on save
Issue #306: Switch Branch Count
Warning:
{
"type": "configuration_mismatch",
"message": "Switch has 3 rules but 4 output connections",
"node": "Switch"
}Status: False positive when using "fallback" mode
Why it occurs: Fallback creates extra output
What to do: Ignore if using fallback intentionally
Issue #338: Credential Validation in Test Mode
Warning:
{
"type": "credentials_invalid",
"message": "Cannot validate credentials without execution context"
}Status: False positive during static validation
Why it occurs: Credentials validated at runtime, not build time
What to do: Ignore - credentials are validated when workflow runs
---
Summary
Always Fix
- ❌ Security warnings
- ❌ Hardcoded credentials
- ❌ SQL injection risks
- ❌ Production workflow errors
Usually Fix
- ⚠️ Error handling (production)
- ⚠️ Retry logic (external APIs)
- ⚠️ Input validation (public webhooks)
- ⚠️ Rate limiting (high volume)
Often Acceptable
- ✅ Error handling (dev/test)
- ✅ Retry logic (internal APIs)
- ✅ Rate limiting (low volume)
- ✅ Query limits (small datasets)
Always Acceptable
- ✅ Known n8n issues (#304, #306, #338)
- ✅ Auto-sanitization warnings
- ✅ Metadata completeness (auto-fixed)
Golden Rule: If you accept a warning, document WHY.
Related Files:
- [SKILL.md](SKILL.md) - Main validation guide
- [ERROR_CATALOG.md](ERROR_CATALOG.md) - Error types and fixes
n8n Validation Expert
Expert guidance for interpreting and fixing n8n validation errors.
Overview
Skill Name: n8n Validation Expert Priority: Medium Purpose: Interpret validation errors and guide systematic fixing through the validation loop
The Problem This Solves
Validation errors are common:
- Validation often requires iteration (79% lead to feedback loops)
- 7,841 validate → fix cycles (avg 23s thinking + 58s fixing)
- 2-3 iterations average to achieve valid configuration
Key insight: Validation is an iterative process, not a one-shot fix!
What This Skill Teaches
Core Concepts
1. Error Severity Levels
- Errors (must fix) - Block execution
- Warnings (should fix) - Don't block but indicate issues
- Suggestions (optional) - Nice-to-have improvements
2. The Validation Loop
- Configure → Validate → Read errors → Fix → Validate again
- Average 2-3 iterations to success
- 23 seconds thinking + 58 seconds fixing per cycle
3. Validation Profiles
minimal- Quick checks, most permissiveruntime- Recommended for most use casesai-friendly- Reduces false positives for AI workflowsstrict- Maximum safety, many warnings
4. Auto-Sanitization System
- Automatically fixes operator structure issues
- Runs on every workflow save
- Fixes binary/unary operator problems
- Adds IF/Switch metadata
5. False Positives
- Not all warnings need fixing
- 40% of warnings are acceptable in context
- Use
ai-friendlyprofile to reduce by 60% - Document accepted warnings
File Structure
n8n-validation-expert/
├── SKILL.md
│ Core validation concepts and workflow
│ - Validation philosophy
│ - Error severity levels
│ - The validation loop pattern
│ - Validation profiles
│ - Common error types
│ - Auto-sanitization system
│ - Workflow validation
│ - Recovery strategies
│ - Best practices
│
├── ERROR_CATALOG.md
│ Complete error reference with examples
│ - 9 error types with real examples
│ - missing_required (45% of errors)
│ - invalid_value (28%)
│ - type_mismatch (12%)
│ - invalid_expression (8%)
│ - invalid_reference (5%)
│ - operator_structure (2%, auto-fixed)
│ - Recovery patterns
│ - Summary with frequencies
│
├── FALSE_POSITIVES.md
│ When warnings are acceptable
│ - Philosophy of warning acceptance
│ - 6 common false positive types
│ - When acceptable vs when to fix
│ - Validation profile strategies
│ - Decision framework
│ - Documentation template
│ - Known n8n issues (#304, #306, #338)
│
└── README.md (this file)
Skill metadata and statisticsTotal: 4 files
Common Error Types
| Error Type | Priority | Auto-Fix | Severity |
|---|---|---|---|
| missing_required | Highest | ❌ | Error |
| invalid_value | High | ❌ | Error |
| type_mismatch | Medium | ❌ | Error |
| invalid_expression | Medium | ❌ | Error |
| invalid_reference | Low | ❌ | Error |
| operator_structure | Low | ✅ | Warning |
Key Insights
1. Validation is Iterative
Don't expect to get it right on the first try. Multiple validation cycles (typically 2-3) are normal and expected!
2. False Positives Exist
Many validation warnings are acceptable in production workflows. This skill helps you recognize which ones to address vs. which to ignore.
3. Auto-Sanitization Works
Certain error types (like operator structure issues) are automatically fixed by n8n. Don't waste time manually fixing these!
4. Profile Matters
ai-friendlyreduces false positives by 60%runtimeis the sweet spot for most use casesstricthas value pre-production but is noisy
5. Error Messages Help
Validation errors include fix guidance - read them carefully!
Usage Examples
Example 1: Basic Validation Loop
// Iteration 1
let config = {
resource: "channel",
operation: "create"
};
const result1 = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// → Error: Missing "name"
// Iteration 2
config.name = "general";
const result2 = validate_node({...});
// → Valid! ✅Example 2: Handling False Positives
// Run validation
const result = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// Fix errors (must fix)
if (!result.valid) {
result.errors.forEach(error => {
console.log(`MUST FIX: ${error.message}`);
});
}
// Review warnings (context-dependent)
result.warnings.forEach(warning => {
if (warning.type === 'best_practice' && isDevWorkflow) {
console.log(`ACCEPTABLE: ${warning.message}`);
} else {
console.log(`SHOULD FIX: ${warning.message}`);
}
});Example 3: Using Auto-Fix
// Check what can be auto-fixed
const preview = n8n_autofix_workflow({
id: "workflow-id",
applyFixes: false // Preview mode
});
console.log(`Can auto-fix: ${preview.fixCount} issues`);
// Apply fixes
if (preview.fixCount > 0) {
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: true
});
}When This Skill Activates
Trigger phrases:
- "validation error"
- "validation failing"
- "what does this error mean"
- "false positive"
- "validation loop"
- "operator structure"
- "validation profile"
Common scenarios:
- Encountering validation errors
- Stuck in validation feedback loops
- Wondering if warnings need fixing
- Choosing the right validation profile
- Understanding auto-sanitization
Integration with Other Skills
Works With:
- n8n MCP Tools Expert - How to use validation tools correctly
- n8n Expression Syntax - Fix invalid_expression errors
- n8n Node Configuration - Understand required fields
- n8n Workflow Patterns - Validate pattern implementations
Complementary:
- Use MCP Tools Expert to call validation tools
- Use Expression Syntax to fix expression errors
- Use Node Configuration to understand dependencies
- Use Workflow Patterns to validate structure
Testing
Evaluations: 4 test scenarios
1. eval-001-missing-required-field.json
- Tests error interpretation
- Guides to get_node
- References ERROR_CATALOG.md
2. eval-002-false-positive.json
- Tests warning vs error distinction
- Explains false positives
- References FALSE_POSITIVES.md
- Suggests ai-friendly profile
3. eval-003-auto-sanitization.json
- Tests auto-sanitization understanding
- Explains operator structure fixes
- Advises trusting auto-fix
4. eval-004-validation-loop.json
- Tests iterative validation process
- Explains 2-3 iteration pattern
- Provides systematic approach
Success Metrics
Before this skill:
- Users confused by validation errors
- Multiple failed attempts to fix
- Frustration with "validation loops"
- Fixing issues that auto-fix handles
- Fixing all warnings unnecessarily
After this skill:
- Systematic error resolution
- Understanding of iteration process
- Recognition of false positives
- Trust in auto-sanitization
- Context-aware warning handling
- 94% success within 3 iterations
Related Documentation
- n8n-mcp MCP Server: Provides validation tools
- n8n Validation API: validate_node, validate_workflow, n8n_autofix_workflow
- n8n Issues: #304 (IF metadata), #306 (Switch branches), #338 (credentials)
Version History
- v1.0 (2025-10-20): Initial implementation
- SKILL.md with core concepts
- ERROR_CATALOG.md with 9 error types
- FALSE_POSITIVES.md with 6 false positive patterns
- 4 evaluation scenarios
Author
Conceived by Romuald Członkowski - www.aiadvisors.pl/en
Part of the n8n-skills meta-skill collection.
Workflow Review Checklist
A severity-tiered audit for reviewing an existing n8n workflow — yours or anyone's. This is different from the validate-as-you-build loop in the main skill: that loop catches schema and shape errors with validate_node / validate_workflow; this checklist catches the silent issues those tools pass clean — antipatterns, security holes, broken-but-valid connections, and missing error paths.
How to use
Pull the workflow first, then walk the list top to bottom. For each item, inspect the actual JSON and decide if it applies. Report findings grouped by severity, each pointing at the canonical skill for the why and the fix.
You're reviewing JSON, not source. n8n_get_workflow returns nodes (with parameters, credentials, type strings like nodes-base.httpRequest) and a connections graph. Phrase findings in JSON terms: "node Route order has no parameters.options.fallbackOutput, so unmatched items drop."
Bare NODE_FAMILY_GOTCHAS.md references in the lists below all point to n8n-node-configuration/NODE_FAMILY_GOTCHAS.md.
| Severity | Meaning | Action |
|---|---|---|
| MUST FIX | Ship-blocker: security hole, broken connection, production-breaking bug. | If the workflow is active, stop it; fix before re-enabling. |
| SHOULD FIX | Real issue: antipattern, missing error handling on a production path, broken contract. | Fix in the next change. |
| NICE TO HAVE | Polish: naming, descriptions, readability. | Clean up opportunistically. |
A review agent should not auto-fix MUST FIX items without user confirmation — security and connection changes have blast radius. Surface the finding, propose the fix, wait for approval.
Contents
---
Cross-cutting first
- [ ] Pull the workflow.
n8n_get_workflow({ id })so every check runs on real JSON, not assumptions. Usestructuremode for a fast graph read,fullwhen you need parameters, andfiltered+nodeNamesto read a single heavy node (e.g. a long Code node) on a large workflow that would otherwise truncate client-side when fetched whole. - [ ] Logic smell test. Trace the happy path once, top to bottom. Does the structure match the workflow's stated purpose? Anything dead, contradictory, or out of place (a write node in a "read-only" flow, a fan-out branch wired nowhere, an HTTP call to an unrelated domain)? → n8n-workflow-patterns
- [ ] Note the trigger type and whether it's active. Severity shifts with both: a webhook/API or unattended schedule needs error paths a manual run doesn't, and an active workflow with broken connections is higher severity.
---
MUST FIX
Credentials and secrets
- [ ] Tokens / API keys / passwords in node text fields (
Bearer xxx,sk-...typed into an HTTP header value, a query param, or any parameter). The credential system is the only correct home. Usen8n_manage_credentialsto inspect/migrate. → n8n-mcp-tools-expert (credential management) - [ ] Secrets stored in Set node values for later
{{ $json.token }}referencing. The secret is in the workflow JSON regardless of how it's read. → n8n-mcp-tools-expert - [ ] Hardcoded credentials inside Code nodes. Same leak surface as text fields. → n8n-code-javascript / n8n-code-python (anti-patterns)
- [ ] Placeholder credential IDs (
"id": "REPLACE_ME") left in thecredentialsblock. n8n renders a permanently disabled selector for unknown IDs. Omit the block when the real ID is unknown. → n8n-node-configuration
Run n8n_audit_instance to surface hardcoded secrets and unauthenticated webhooks across the instance automatically. → n8n-validation-expertSQL / query injection
- [ ] User input interpolated into a query string. Any DB node with
{{ ... }}insideparameters.query— n8n substitutes it into the SQL before the driver binds parameters, so it's an injection vector. Use$1, $2placeholders +parameters.options.queryReplacement(Postgres/MySQL); object filters for Mongo. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Database)
Connection bugs (valid but broken)
- [ ] Merge with 3+ sources but `numberOfInputs` still 2. Third source silently drops. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Merge)
- [ ] Merge index off-by-one.
parameters.useDataOfInputis 1-indexed; the wire sits atconnections.<source>.main[N-1]. Mismatch passes through the wrong source silently. Verify withn8n_get_workflow. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Merge) - [ ] Error output enabled but unwired, or wired but not enabled. If
parameters.onErroriscontinueErrorOutputbutconnections.<node>.main[1]is empty, the error path goes nowhere; if a node feeds an error branch butonErrorisn't set, the branch is unreachable and a failure halts the workflow. (Dedicated error-handling guidance is in flight; for now treat this as a connection/config audit.) → n8n-validation-expert
Switch
- [ ] No fallback output. Without
parameters.options.fallbackOutput: "extra", unmatched items drop silently. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Switch)
Webhook API workflows
- [ ] Webhook performing a sensitive action with `parameters.authentication: "none"`. "Sensitive" = mutates state, sends external messages, hits production data, triggers paid actions. Anyone with the URL can fire it. Set
authenticationtobasicAuth/headerAuthwith a matching credential. → n8n-workflow-patterns (webhook), n8n-mcp-tools-expert (credentials) - [ ] Error branch returns HTTP 200.
responseCodedefaults to 200 on every Respond node, including error paths. The caller sees success while the body says failure. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Webhook)
---
SHOULD FIX
Set-node antipattern
- [ ] Set node feeding 0 or 1 downstream consumer. The most common antipattern. Delete it and inline the expression at the consumer. Exceptions: 2+ consumers of a non-trivial derived value, or a sub-workflow's final Return node. → n8n-expression-syntax ("The Set-node antipattern and branch convergence")
- [ ] Set node building an email / Slack body. Build the body inline in the comms node's body field. → n8n-expression-syntax
- [ ] Set node mapping fields right before a write node. Map directly in the write node's per-field expression slots. → n8n-node-configuration
- [ ] Multiple consecutive Set nodes each defining one field. Collapse into one, or eliminate. → n8n-expression-syntax
Code-node antipattern
- [ ] Code node doing pure single-item shaping (
.map/.filter/.find, field rename, optional chaining). Use an expression or an Edit Fields arrow-function IIFE — same result, ~100x faster, more readable. → n8n-code-javascript (the transform gatekeeper) - [ ] Code node using `crypto.createHash` / `crypto.createHmac`. Use the native Crypto node (
nodes-base.crypto). Recurring slip. → n8n-code-javascript - [ ] Code node parsing XML / SOAP / RSS. Use the native XML node (
nodes-base.xml) + Edit Fields for extraction. → n8n-code-javascript - [ ] Code node + Set node combo (Set builds inputs, Code transforms). One Edit Fields arrow-function IIFE does both. → n8n-code-javascript
- [ ] Python Code node where JS would do. JS is recommended for ~95% of cases; reserve Python for its standard-library strengths (regex, hashlib, statistics) when the user asked for it. → n8n-code-python
Expression discipline
- [ ] `$json.x` deep in a branchy / multi-step workflow. Switch to
$('Source Node').item.json.xfor refactor stability; the$jsonform breaks silently when an intermediate is inserted or context is cleared. → n8n-expression-syntax (non-negotiable) - [ ] Branches converge with `$json` references downstream. Whichever branch fired last wins, non-deterministically. Insert a NoOp (
Combine Inputs) at the merge and reference it by name; use a Set to normalize if the branch shapes differ. → n8n-expression-syntax - [ ] DateTime node used for date math/formatting. Use a Luxon expression inline (
DateTime.fromISO(...).toFormat(...)). → n8n-expression-syntax - [ ] `$env.X` in any expression. Doesn't work, throws at runtime. Use
$vars.X(paid plans), a Data Table, or a credential for secrets. → n8n-expression-syntax - [ ] `.all().map()/filter()/reduce()` aggregation without `executeOnce: true` on the node. It re-runs the full aggregation per input item — wasted work, and N identical outputs where one was expected. (Leave
executeOnceoff when.all()is a per-item lookup keyed by the current item.) → n8n-expression-syntax
Slack / comms
- [ ] Block Kit passed as a bare array. Posts as plain text, silently. Wrap as
={{ { "blocks": ... } }}. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Slack) - [ ] Thread reply posting as a top-level message —
thread_tsnot set or in the wrong place. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Slack) - [ ] Operation set from the UI display name (
"send") rather than the internal value ("post"). Confirm withget_node. → n8n-node-configuration
Database
- [ ] `select` / query with no-match path feeding an IF, but `alwaysOutputData` not set. No match = zero items = the IF never fires. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Database)
- [ ] Multi-step writes needing atomicity split across nodes. No cross-node transaction exists; collapse into one
executeQuerywithoptions.queryBatching: "transaction". → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Database) - [ ] Write node (INSERT/UPDATE/DELETE) followed by a node expecting its output, without `alwaysOutputData`. Writes often return 0 items and stall the chain. → n8n-node-configuration
Webhook / Respond to Webhook
- [ ] `responseMode` left at `onReceived` for a request/response API. Caller never sees the computed result; use
responseNode. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Webhook) - [ ] Generic 500 for every failure. Map status codes: 400 validation, 401/403 auth, 409 conflict, 429 rate limit. → n8n-node-configuration
- [ ] `respondWith: "json"` body built with `JSON.stringify(...)`. Double-encodes. Pass the object literal in expression mode. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Webhook)
- [ ] Fallible nodes (HTTP/DB/API) on a webhook path with no error branch. A failure halts the workflow and the caller gets n8n's generic error. (Until a dedicated error-handling skill lands, wire `onError: "continueErrorOutput"` + a 5xx Respond, and validate the wiring.) → n8n-workflow-patterns (webhook), n8n-validation-expert
HTTP Request
- [ ] Auth header typed into `headerParameters` instead of a credential. Use Bearer Auth / Header Auth credentials. → n8n-node-configuration, n8n-mcp-tools-expert
- [ ] Headers set via both `headerParameters` and a credential's header auth. They conflict. → n8n-node-configuration
- [ ] Network-calling node (HTTP/comms/DB/AI) without `retryOnFail`. Transient 429s and blips surface as hard failures. (Transient-failure handling folds into node config for now.) → n8n-node-configuration
Schedule trigger
- [ ] Business-critical schedule with no explicit workflow timezone. DST and instance moves shift timing. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Schedule)
- [ ] Schedule-triggered workflow not idempotent. Restarts can miss runs and re-runs can double-fire. → n8n-node-configuration → NODE_FAMILY_GOTCHAS.md (Schedule)
Structure and patterns
- [ ] Workflow built without a recognizable pattern where one fits (webhook, HTTP API, database, AI, scheduled, batch). Reshape to the proven pattern. → n8n-workflow-patterns
- [ ] Fan-out branches assumed to run in parallel. n8n runs them sequentially. Real concurrency needs sub-workflow dispatch. (Sub-workflow guidance is pending a dedicated skill; note the limitation for now.) → n8n-workflow-patterns
- [ ] Large-item-count flow doing per-item work where batching/aggregation would cut overhead. → n8n-workflow-patterns
AI Agent / Code Tool (when present)
- [ ] Custom Code Tool returning a non-string (
[{json:{...}}]) or using$fromAI/$input/$helpers— none exist in the Code Tool sandbox; the return must be a string. → n8n-code-tool - [ ] Tool with a generic/empty name or description. The model can't tell when to call it; descriptions are part of the prompt. Use verb-first specific names. (Deeper agent guidance is pending a dedicated skill.) → n8n-code-tool
---
NICE TO HAVE
Naming
- [ ] Generic node names (
HTTP Request1,Set2,Postgres1). A runtime failure onFetch order detailslocalizes the break instantly;HTTP Request3tells the operator nothing. Rename to describe what the node does in this workflow. → n8n-workflow-patterns - [ ] Workflow name not verb-first (
Send weekly customer report, notCustomer report sender). Sentence case, no emojis, no trailing version numbers. → n8n-workflow-patterns
Readability
- [ ] Workflow `description` empty or one line. Two sentences: what it does and why it exists (the "why" is the part that otherwise gets lost). → n8n-workflow-patterns
- [ ] Code node with no one-line comment explaining why simpler tools weren't used. → n8n-code-javascript
- [ ] Multi-line expression that isn't indented or commented. Most n8n users aren't coders — format it like real code. → n8n-expression-syntax
---
Reporting findings
Group by severity, then by domain. For each finding give the node(s) affected, a one-sentence description, and the canonical skill for the fix.
MUST FIX
Security
- Node `Send webhook`: bearer token typed into headerParameters value. -> n8n-mcp-tools-expert (credentials)
- Node `Lookup user`: {{ $json.email }} interpolated into parameters.query. -> NODE_FAMILY_GOTCHAS.md (Database)
Connections
- Node `Merge customer + Stripe`: 3 sources wired but parameters.numberOfInputs = 2; third drops. -> NODE_FAMILY_GOTCHAS.md (Merge)
SHOULD FIX
Set-node antipattern
- Node `Set customer_id`: feeds one consumer; inline at `Lookup customer`. -> n8n-expression-syntax
...Related skills
How it compares
Pick n8n-validation-expert over generic debugging skills when the artifact is n8n workflow JSON and validation tool output—not application stack traces.
FAQ
Who is n8n-validation-expert for?
Developers fixing n8n workflow validation errors and distinguishing real issues from false positives.
When should I use it?
Whenever n8n validation output blocks activation or shows confusing warnings.
Is it safe to install?
Review Security Audits panel; it guides workflow fixes without executing untrusted n8n code.