data/skills/n8n-mcp-tools-expert/VALIDATION_GUIDE.md
Complete guide for validating node configurations and workflows.
Validate early, validate often
Validation is typically iterative with validate → fix cycles
The validate_node tool provides all validation capabilities with different modes.
Speed: <50ms
Use when: Checking what fields are required
validate_node({
nodeType: "nodes-base.slack",
config: {}, // Empty to see all required fields
mode: "minimal"
})
Returns:
{
"valid": true, // Usually true (most nodes have no strict requirements)
"missingRequiredFields": []
}
When to use: Planning configuration, seeing basic requirements
Speed: <100ms
Use when: Validating actual configuration before deployment
validate_node({
nodeType: "nodes-base.slack",
config: {
resource: "channel",
operation: "create",
channel: "general"
},
profile: "runtime" // Recommended!
})
// mode="full" is the default
The profile controls which advisory findings you see — not how likely the validator is to be
wrong. What flips valid: false (real errors) and what counts as a security/deprecation warning is
the same under every profile; the profiles differ only in how many best-practice advisories ride
along (n8n-mcp ≥ 2.63.0). Choose by how much lint you want at this stage:
minimal - Required fields only
runtime - Errors + security/deprecation warnings (RECOMMENDED)
typeVersion notes
(at most a single top-level "add error handling" suggestion)ai-friendly - runtime + best-practice advisories
runtime reports, plus advisory notes: outdated-typeVersion suggestions,
per-node "without error handling" warnings, resource-locator cachedResultName advice,
long-chain hintsvalid: falsestrict - ai-friendly + strict-only checks
ai-friendly reports, plus checks like "Property 'X' won't be used" for
leftover parameters hidden by the current settingsThese are advisory tiers, not accuracy tiers.
ai-friendlyis not "more tolerant" andstrictis not "more likely to be wrong" — a config that isvalidunderruntimestaysvalidunderstrict;strictjust adds more suggestions and warnings to read.
{
"nodeType": "nodes-base.slack",
"workflowNodeType": "n8n-nodes-base.slack",
"displayName": "Slack",
"valid": false,
"errors": [
{
"type": "missing_required",
"property": "name",
"message": "Channel name is required",
"fix": "Provide a channel name (lowercase, no spaces, 1-80 characters)"
}
],
"warnings": [
{
"type": "best_practice",
"property": "errorHandling",
"message": "Slack API can have rate limits",
"suggestion": "Add onError: 'continueRegularOutput' with retryOnFail"
}
],
"suggestions": [],
"summary": {
"hasErrors": true,
"errorCount": 1,
"warningCount": 1,
"suggestionCount": 0
}
}
missing_required - Must fix (flips valid:false)invalid_value - Must fix (flips valid:false)type_mismatch - Must fix (flips valid:false)best_practice - Advisory warning; surfaces under ai-friendly/strict (security/deprecation warnings surface under every profile)suggestion - Optional improvement; surfaces under ai-friendly/strictThe best_practice warning shown above (rate-limit / error-handling advice) is one of the
advisories gated to ai-friendly/strict — under runtime this same config reports the error
with no such warning.
Speed: 100-500ms
Use when: Checking complete workflow before execution
Syntax:
validate_workflow({
workflow: {
nodes: [...], // Array of nodes
connections: {...} // Connections object
},
options: {
validateNodes: true, // Default: true
validateConnections: true, // Default: true
validateExpressions: true, // Default: true
profile: "runtime" // For node validation
}
})
Validates:
Returns: Comprehensive validation report with errors, warnings, suggestions
// Validate workflow already in n8n
n8n_validate_workflow({
id: "workflow-id",
options: {
validateNodes: true,
validateConnections: true,
validateExpressions: true,
profile: "runtime"
}
})
Typical cycle: 23s thinking, 58s fixing
1. Configure node
↓
2. validate_node (23s thinking about errors)
↓
3. Fix errors
↓
4. validate_node again (58s fixing)
↓
5. Repeat until valid
Example:
// Iteration 1
let config = {
resource: "channel",
operation: "create"
};
const result1 = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// → Error: Missing "name"
// Iteration 2 (~58s later)
config.name = "general";
const result2 = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// → Valid!
When it runs: On ANY workflow update (create or update_partial)
What it fixes (automatically on ALL nodes):
singleValuesingleValue: trueconditions.options metadataconditions.options for all rulesWhat it CANNOT fix:
Example:
// Before auto-sanitization
{
"type": "boolean",
"operation": "equals",
"singleValue": true // Binary operators shouldn't have this
}
// After auto-sanitization (automatic!)
{
"type": "boolean",
"operation": "equals"
// singleValue removed automatically
}
Recovery tools:
cleanStaleConnections operation - removes broken connectionsn8n_autofix_workflow({id}) - preview/apply fixesUse when: Validation errors need automatic fixes
// Preview fixes (default - doesn't apply)
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: false, // Preview mode
confidenceThreshold: "medium" // high, medium, low
})
// Apply fixes
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: true
})
Fix Types:
expression-format - Fix missing = prefix in expressionstypeversion-correction - Downgrade unsupported typeVersionserror-output-config - Remove conflicting onError settingsnode-type-correction - Fix unknown node types via similarity matching (90%+ confidence)webhook-missing-path - Generate UUIDs for webhook nodes missing pathstypeversion-upgrade - Smart upgrade nodes to latest versions with auto-migrationversion-migration - Guidance for complex breaking changes (manual steps)Confidence Threshold: high (90%+), medium (70-89%, default), low (any)
Post-update guidance: Check postUpdateGuidance in the response for version upgrade migration steps.
Binary operators (compare two values):
singleValue: trueUnary operators (check single value):
singleValue: trueAuto-sanitization fixes these automatically!
1. Read error message carefully
2. Separate real errors (they flip valid:false) from advisory notes
3. Fix the errors
4. Validate again
5. Iterate until clean
Only findings in errors flip valid: false. warnings and suggestions are advice — an
outdated-typeVersion note or a "without error handling" suggestion under ai-friendly/strict
does not make the workflow invalid, so treat them as a to-review list, not a blocker.
"Required field missing" / "Required property 'X' cannot be empty" → Add the field with a real value. This is a true error even when the field looks optional — n8n's own publish validation rejects the same empty value, so the workflow will not save.
"Invalid value" → Check allowed values in get_node output. Fires only on an explicitly wrong enum value; an omitted operation on a multi-resource node (Gmail, Slack, Telegram…) is no longer flagged (n8n-mcp ≥ 2.63.0 resolves the correct per-resource default before checking).
"Type mismatch" → Convert to correct type (string/number/boolean)
"Code cannot be empty"
→ Fill in the Code node's jsCode/pythonCode. Kept as a true error — n8n refuses to run an
empty Code node.
Operator shapes (
singleValue, IF/Switchconditions.optionsmetadata) are no longer validation errors. The save-time sanitizer normalizes them (see Auto-Sanitization), and validate-only calls leave them alone — so you will not see "Cannot have singleValue" or "Missing operator metadata" from the validator anymore.
There is no standing list of validator false positives to ignore (n8n-mcp ≥ 2.63.0 removed the
classes that used to require it — template literals inside {{ }}, optional chaining ?.,
string-keyed bracket access like $json['some-prop'], legacy IF v1 condition shapes, the
Webhook → Respond-to-Webhook pattern, and outdated-but-supported typeVersions all validate cleanly
now). If something lands in errors, treat it as real. If you want the leanest output while
building, validate under runtime (errors + security/deprecation only); switch to
ai-friendly/strict when you want the best-practice advisories.
mode: "minimal" for quick checksn8n_autofix_workflow for bulk fixesactivateWorkflow operation)// Step 1: Get node requirements (quick check)
validate_node({
nodeType: "nodes-base.slack",
config: {},
mode: "minimal"
});
// → Know what's required
// Step 2: Configure node
const config = {
resource: "message",
operation: "post",
channel: "#general",
text: "Hello!"
};
// Step 3: Validate configuration (full validation)
const result = validate_node({
nodeType: "nodes-base.slack",
config,
profile: "runtime"
});
// Step 4: Check result
if (result.valid) {
console.log("Configuration valid!");
} else {
console.log("Errors:", result.errors);
// Fix and validate again
}
// Step 5: Validate in workflow context
validate_workflow({
workflow: {
nodes: [{...config as node...}],
connections: {...}
}
});
// Step 6: Apply auto-fixes if needed
n8n_autofix_workflow({
id: "workflow-id",
applyFixes: true
});
Key Points:
n8n_autofix_workflow for automatic fixesTool Selection:
The eight most common tool-usage mistakes, with WRONG vs CORRECT examples.
Problem: "Node not found" error
// WRONG
get_node({nodeType: "slack"}) // Missing prefix
get_node({nodeType: "n8n-nodes-base.slack"}) // Wrong prefix
// CORRECT
get_node({nodeType: "nodes-base.slack"})
Problem: Huge payload, slower response, token waste
// WRONG - Returns 3-8K tokens, use sparingly
get_node({nodeType: "nodes-base.slack", detail: "full"})
// CORRECT - Returns 1-2K tokens, covers 95% of use cases
get_node({nodeType: "nodes-base.slack"}) // detail="standard" is default
get_node({nodeType: "nodes-base.slack", detail: "standard"})
When to use detail="full":
Better alternatives:
get_node({detail: "standard"}) - for operations list (default)get_node({mode: "docs"}) - for readable documentationget_node({mode: "search_properties", propertyQuery: "auth"}) - for specific propertyProblem: Missing real errors, or drowning in advisory notes at the wrong stage
Profiles (they gate advisory volume, not accuracy — see Validation Profiles):
minimal - Required fields only (fast, leanest)runtime - Errors + security/deprecation warnings (recommended for pre-deployment; decides valid/invalid)ai-friendly - runtime + best-practice advisories (outdated-typeVersion, error-handling suggestions…)strict - ai-friendly + strict-only checks (e.g. "property won't be used")// WRONG - Uses default profile
validate_node({nodeType, config})
// CORRECT - Explicit profile
validate_node({nodeType, config, profile: "runtime"})
What happens: ALL nodes sanitized on ANY workflow update
Auto-fixes:
Cannot fix:
// After ANY update, auto-sanitization runs on ALL nodes
n8n_update_partial_workflow({id, operations: [...]})
// → Automatically fixes operator structures
Problem: Complex sourceIndex calculations for multi-output nodes
Old way (manual):
// IF node connection
{
type: "addConnection",
source: "IF",
target: "Handler",
sourceIndex: 0 // Which output? Hard to remember!
}
New way (smart parameters):
// IF node - semantic branch names
{
type: "addConnection",
source: "IF",
target: "True Handler",
branch: "true" // Clear and readable!
}
{
type: "addConnection",
source: "IF",
target: "False Handler",
branch: "false"
}
// Switch node - semantic case numbers
{
type: "addConnection",
source: "Switch",
target: "Handler A",
case: 0
}
Problem: Using parameters instead of updates
// WRONG
n8n_update_partial_workflow({
id: "wf-123",
operations: [{
type: "updateNode",
nodeName: "HTTP Request",
parameters: {url: "..."} // ❌ Wrong key
}]
})
// CORRECT
n8n_update_partial_workflow({
id: "wf-123",
operations: [{
type: "updateNode",
nodeName: "HTTP Request",
updates: {url: "..."} // ✅ Correct key
}]
})
Problem: Credentials not attaching to nodes
// WRONG - credentials as flat object
updates: {credentials: "myApiKey"}
// CORRECT - credentials nested by type with id and name
updates: {
credentials: {
httpHeaderAuth: {
id: "abc123",
name: "My API Key"
}
}
}
Problem: Less helpful tool responses
// WRONG - No context for response
n8n_update_partial_workflow({
id: "abc",
operations: [{type: "addNode", node: {...}}]
})
// CORRECT - Better AI responses
n8n_update_partial_workflow({
id: "abc",
intent: "Add error handling for API failures",
operations: [{type: "addNode", node: {...}}]
})
Related: