data/skills/n8n-node-configuration/DEPENDENCIES.md
Deep dive into n8n property dependencies and displayOptions mechanism.
Definition: Rules that control when fields are visible or required based on other field values.
Mechanism: displayOptions in node schema
Purpose:
{
"name": "fieldName",
"type": "string",
"displayOptions": {
"show": {
"otherField": ["value1", "value2"]
}
}
}
Translation: Show fieldName when otherField equals "value1" OR "value2"
Show field when condition matches:
{
"name": "body",
"displayOptions": {
"show": {
"sendBody": [true]
}
}
}
Meaning: Show body when sendBody = true
Hide field when condition matches:
{
"name": "advanced",
"displayOptions": {
"hide": {
"simpleMode": [true]
}
}
}
Meaning: Hide advanced when simpleMode = true
{
"name": "body",
"displayOptions": {
"show": {
"sendBody": [true],
"method": ["POST", "PUT", "PATCH"]
}
}
}
Meaning: Show body when:
sendBody = true ANDmethod IN (POST, PUT, PATCH)All conditions must match (AND logic)
{
"name": "someField",
"displayOptions": {
"show": {
"method": ["POST", "PUT", "PATCH"]
}
}
}
Meaning: Show someField when:
method = POST ORmethod = PUT ORmethod = PATCHAny value matches (OR logic)
Use case: Optional feature flag
Example: HTTP Request sendBody
// Field: sendBody (boolean)
{
"name": "sendBody",
"type": "boolean",
"default": false
}
// Field: body (depends on sendBody)
{
"name": "body",
"displayOptions": {
"show": {
"sendBody": [true]
}
}
}
Flow:
Use case: Different operations show different fields
Example: Slack message operations
// Operation: post
{
"name": "channel",
"displayOptions": {
"show": {
"resource": ["message"],
"operation": ["post"]
}
}
}
// Operation: update
{
"name": "messageId",
"displayOptions": {
"show": {
"resource": ["message"],
"operation": ["update"]
}
}
}
Flow:
Use case: Different types need different fields
Example: IF node conditions
// String operations
{
"name": "value2",
"displayOptions": {
"show": {
"conditions.string.0.operation": ["equals", "notEquals", "contains"]
}
}
}
// Unary operations (isEmpty) don't show value2
{
"displayOptions": {
"hide": {
"conditions.string.0.operation": ["isEmpty", "isNotEmpty"]
}
}
}
Use case: HTTP methods have different options
Example: HTTP Request
// Query parameters (all methods can have)
{
"name": "queryParameters",
"displayOptions": {
"show": {
"sendQuery": [true]
}
}
}
// Body (only certain methods)
{
"name": "body",
"displayOptions": {
"show": {
"sendBody": [true],
"method": ["POST", "PUT", "PATCH", "DELETE"]
}
}
}
// Find properties related to "body"
get_node({
nodeType: "nodes-base.httpRequest",
mode: "search_properties",
propertyQuery: "body"
});
// Get complete schema with displayOptions
get_node({
nodeType: "nodes-base.httpRequest",
detail: "full"
});
✅ Use when:
❌ Don't use when:
get_node with standard detail)Scenario: Configuring POST with JSON body
Step 1: Set method
{
"method": "POST"
// → sendBody becomes visible
}
Step 2: Enable body
{
"method": "POST",
"sendBody": true
// → body field becomes visible AND required
}
Step 3: Configure body
{
"method": "POST",
"sendBody": true,
"body": {
"contentType": "json"
// → content field becomes visible AND required
}
}
Step 4: Add content
{
"method": "POST",
"sendBody": true,
"body": {
"contentType": "json",
"content": {
"name": "John",
"email": "[email protected]"
}
}
}
// ✅ Valid!
Dependency chain:
method=POST
→ sendBody visible
→ sendBody=true
→ body visible + required
→ body.contentType=json
→ body.content visible + required
Scenario: String comparison with different operators
Binary operator (equals):
{
"conditions": {
"string": [
{
"operation": "equals"
// → value1 required
// → value2 required
// → singleValue should NOT be set
}
]
}
}
Unary operator (isEmpty):
{
"conditions": {
"string": [
{
"operation": "isEmpty"
// → value1 required
// → value2 should NOT be set
// → singleValue should be true (auto-added)
}
]
}
}
Dependency table:
| Operator | value1 | value2 | singleValue |
|---|---|---|---|
| equals | Required | Required | false |
| notEquals | Required | Required | false |
| contains | Required | Required | false |
| isEmpty | Required | Hidden | true |
| isNotEmpty | Required | Hidden | true |
Scenario: Different Slack operations show different fields
// post message
{
"resource": "message",
"operation": "post"
// Shows: channel (required), text (required), attachments, blocks
}
// update message
{
"resource": "message",
"operation": "update"
// Shows: messageId (required), text (required), channel (optional)
}
// delete message
{
"resource": "message",
"operation": "delete"
// Shows: messageId (required), channel (required)
// Hides: text, attachments, blocks
}
// get message
{
"resource": "message",
"operation": "get"
// Shows: messageId (required), channel (required)
// Hides: text, attachments, blocks
}
Field visibility matrix:
| Field | post | update | delete | get |
|---|---|---|---|---|
| channel | Required | Optional | Required | Required |
| text | Required | Required | Hidden | Hidden |
| messageId | Hidden | Required | Required | Required |
| attachments | Optional | Optional | Hidden | Hidden |
| blocks | Optional | Optional | Hidden | Hidden |
Definition: Dependencies within object properties
Example: HTTP Request body.contentType controls body.content structure
{
"body": {
"contentType": "json",
// → content expects JSON object
"content": {
"key": "value"
}
}
}
{
"body": {
"contentType": "form-data",
// → content expects form fields array
"content": [
{
"name": "field1",
"value": "value1"
}
]
}
}
Strategy: Configure parent first, then children
// Step 1: Parent
{
"body": {
"contentType": "json" // Set parent first
}
}
// Step 2: Children (structure determined by parent)
{
"body": {
"contentType": "json",
"content": { // JSON object format
"key": "value"
}
}
}
Operator structure issues (IF/Switch nodes):
Example: singleValue property
// You configure (missing singleValue)
{
"type": "boolean",
"operation": "isEmpty"
// Missing singleValue
}
// Auto-sanitization adds it
{
"type": "boolean",
"operation": "isEmpty",
"singleValue": true // ✅ Added automatically
}
Missing required fields:
// You configure (missing channel)
{
"resource": "message",
"operation": "post",
"text": "Hello"
// Missing required field: channel
}
// Auto-sanitization does NOT add
// You must add it yourself
{
"resource": "message",
"operation": "post",
"channel": "#general", // ← You must add
"text": "Hello"
}
Error:
{
"type": "missing_required",
"property": "body",
"message": "body is required"
}
But you don't see body field in configuration!
Solution:
// Check field dependencies using search_properties
get_node({
nodeType: "nodes-base.httpRequest",
mode: "search_properties",
propertyQuery: "body"
});
// Find that body shows when sendBody=true
// Add sendBody
{
"method": "POST",
"sendBody": true, // ← Now body appears!
"body": {...}
}
Scenario:
// Working configuration
{
"resource": "message",
"operation": "post",
"channel": "#general",
"text": "Hello"
}
// Change operation
{
"resource": "message",
"operation": "update", // Changed
"channel": "#general", // Still here
"text": "Updated" // Still here
// Missing: messageId (required for update!)
}
Validation error: "messageId is required"
Why: Different operation = different required fields
Solution:
// Check requirements for new operation
get_node({
nodeType: "nodes-base.slack"
});
// Configure for update operation
{
"resource": "message",
"operation": "update",
"messageId": "1234567890", // Required for update
"text": "Updated",
"channel": "#general" // Optional for update
}
Scenario: Field hidden by dependencies after validation
Example:
// Configure
{
"method": "GET",
"sendBody": true, // ❌ GET doesn't support body
"body": {...} // This will be stripped
}
// After save
{
"method": "GET"
// body removed because method=GET hides it
}
Solution: Respect dependencies from the start
// Correct approach - check property dependencies
get_node({
nodeType: "nodes-base.httpRequest",
mode: "search_properties",
propertyQuery: "body"
});
// See that body only shows for POST/PUT/PATCH/DELETE
// Use correct method
{
"method": "POST",
"sendBody": true,
"body": {...}
}
Example: Channel can be string OR expression
// Option 1: String
{
"channel": "#general"
}
// Option 2: Expression
{
"channel": "={{$json.channelName}}"
}
// Validation accepts both
Example: Use either ID or name, not both
// Use messageId
{
"messageId": "1234567890"
// name not needed
}
// OR use messageName
{
"messageName": "thread-name"
// messageId not needed
}
// Dependencies ensure only one is required
Example: Simple mode vs advanced mode
// Simple mode
{
"mode": "simple",
"text": "{{$json.message}}"
// Advanced fields hidden
}
// Advanced mode
{
"mode": "advanced",
"attachments": [...],
"blocks": [...],
"metadata": {...}
// Simple field hidden, advanced fields shown
}
Check dependencies when stuck
get_node({nodeType: "...", mode: "search_properties", propertyQuery: "..."});
Configure parent properties first
// First: method, resource, operation
// Then: dependent fields
Validate after changing operation
// Operation changed → requirements changed
validate_node({nodeType: "...", config: {...}, profile: "runtime"});
Read validation errors for dependency hints
Error: "body required when sendBody=true"
→ Hint: Set sendBody=true to enable body
Don't ignore dependency errors
// Error: "body not visible" → Check displayOptions
Don't hardcode all possible fields
// Bad: Adding fields that will be hidden
Don't assume operations are identical
// Each operation has unique requirements
Key Concepts:
displayOptions control field visibilityshow = field appears when conditions matchhide = field disappears when conditions matchCommon Patterns:
Troubleshooting:
Tools:
get_node({mode: "search_properties"}) - Find property dependenciesget_node({detail: "full"}) - See complete schema with displayOptionsget_node - See operation requirements (standard detail)Related Files: