How n8n-mcp Expression Validation Detects Syntax Errors and Broken References

The n8n-mcp expression validator implements a four-stage pipeline that first detects malformed brackets and nested expressions, then extracts variable references, validates them against supported n8n syntax, and finally verifies that every $node or $items reference points to an existing node in the workflow.

The ExpressionValidator service in the czlonkowski/n8n-mcp repository provides comprehensive static analysis for n8n-style expressions before they execute. By catching syntax errors and undefined node references early, it prevents runtime failures in MCP tool configurations.

The ExpressionValidator Pipeline

The validation process follows a strict sequence defined in src/services/expression-validator.ts. The entry point validateExpression (lines 43‑84) orchestrates the workflow by initializing a result object, running syntax checks, extracting expression blocks, and delegating to specialized validators. Only when all stages pass without error does the validator set valid: true.

Detecting Syntax Errors with checkSyntaxErrors

Before parsing variables, the validator runs checkSyntaxErrors (lines 88‑115) to catch structural problems that would break n8n's expression engine.

This method detects three critical syntax failures:

  • Unmatched brackets – Missing opening {{ or closing }} delimiters
  • Nested expressions – Invalid patterns like {{ {{ $json }} }} which n8n does not support
  • Empty expressions – Blocks containing only whitespace between delimiters ({{ }})

If any of these conditions are found, the validator immediately populates the errors array and skips further processing of the affected expression.

Extracting Expression Blocks

Once basic syntax passes, extractExpressions (lines 119‑128) uses the global EXPRESSION_PATTERN regular expression (/{{\s*([\s\S]+?)\s*}}/g) to pull the inner content from every {{ … }} block in the input string. This regex captures the raw JavaScript-like code while stripping surrounding whitespace, creating an array of expressions to validate individually.

Variable Analysis and Common Mistakes

The validateSingleExpression method (lines 134‑203) performs deep analysis on each extracted expression. It scans for supported n8n variables including $json, $node, $input, and $items, recording usage in usedVariables and usedNodes arrays.

During this scan, the validator emits context-aware warnings. For example, it flags $json usage when hasInputData is false in the ExpressionContext, or warns when suspicious property names like .invalid, .undefined, or .null are accessed.

The method delegates style-related checks to checkCommonMistakes (lines 205‑250), which detects:

  • Missing $ prefix – Variables without the required dollar sign
  • Non-numeric array indexes – Bracket notation on $json using string keys instead of numbers
  • Python-style syntax – Single-quoted bracket notation like $json['prop']
  • Optional chaining – Unsupported ?. operators
  • Template literals – Interpolated strings using ${…} syntax

Verifying Node References

After collecting all usedNodes during variable analysis, checkNodeReferences (lines 255‑267) validates each node name against the availableNodes list provided in the ExpressionContext. Any $node["Name"] or $items("Name", …) reference pointing to a node not present in the workflow generates a specific error message: "Referenced node \"Name\" not found in workflow".

This stage ensures broken references are caught before the workflow attempts to execute, preventing undefined data access errors at runtime.

Recursive Validation for Complex Parameters

For validating entire node configurations, validateNodeExpressions (lines 270‑289) and validateParametersRecursive (lines 292‑342) traverse complex parameter objects. These methods:

  1. Walk through nested objects and arrays
  2. Call validateExpression on every string containing {{
  3. Aggregate errors and warnings with full path context (e.g., fieldC.broken)
  4. Track visited objects via a WeakSet to prevent infinite loops from circular references

This recursive approach ensures that expressions buried deep in nested configuration objects receive the same validation as top-level parameters.

Summary

  • Layered validation catches syntax errors before attempting variable resolution
  • Regular expression patterns identify n8n-specific variables and common mistakes like missing $ prefixes or unsupported optional chaining
  • Context-aware checking validates that $json usage matches available input data and loop contexts
  • Reference verification confirms every $node and $items target exists in the workflow's availableNodes list
  • Recursive traversal enables validation of deeply nested parameter objects while preventing circular reference loops

Frequently Asked Questions

How does the validator distinguish between valid expressions and plain text?

The validator uses the global regular expression /{{\s*([\s\S]+?)\s*}}/g defined in src/services/expression-validator.ts to scan for n8n's double-bracket delimiters. Only content within these delimiters is extracted and validated; plain text outside the brackets passes through without inspection.

What happens if an expression references a node that exists but provides no data?

The current validation only checks for node existence via checkNodeReferences, not data availability. The ExpressionContext accepts an availableNodes array, and as long as the referenced name appears in that list, validation passes. Runtime data availability checks occur later during actual n8n execution.

Can the validator catch JavaScript syntax errors inside expressions?

The validator detects n8n-specific syntax issues like nested brackets, empty expressions, and unsupported features (template literals, optional chaining). However, it does not execute a full JavaScript parser. Malformed JS syntax inside valid {{ }} blocks may pass validation but fail at runtime during n8n's expression evaluation.

How do I validate expressions across an entire node's parameters?

Use ExpressionValidator.validateNodeExpressions(parameters, context) (lines 270‑289). This method recursively traverses the parameter object, validates every string containing {{, and returns aggregated errors with full path information (e.g., headers.Authorization: Referenced node "API" not found).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →