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

> Discover how n8n-mcp expression validation detects syntax errors and broken references through its four-stage pipeline ensuring workflow integrity and preventing runtime issues.

- Repository: [Romuald Członkowski/n8n-mcp](https://github.com/czlonkowski/n8n-mcp)
- Tags: deep-dive
- Published: 2026-03-24

---

**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`](https://github.com/czlonkowski/n8n-mcp/blob/main/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`](https://github.com/czlonkowski/n8n-mcp/blob/main/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`).