VM expression engine error handling aligned with legacy
The VM expression engine was crashing on errors that the legacy engine would silently swallow. A fix ensures both engines handle paired-item resolution failures consistently.
The n8n expression engine runs in two modes: legacy and VM-isolated. When an expression fails, both engines wrap it in a try-catch that calls an error handler named E(). The problem was that the VM engine's E() was re-throwing all non-TypeError errors, while the legacy engine silently swallowed anything that wasn't an ExpressionError or ExpressionExtensionError. This caused many tests in the VM expression E2E nightly job to fail—expressions that returned undefined gracefully in legacy mode were crashing in VM mode. The fix aligns the VM handler's logic with the legacy handler: re-throw expression-specific errors, swallow everything else. One wrinkle: errors crossing the VM boundary arrive as sentinel objects rather than class instances, so the check uses error names instead of instanceof. The change is isolated to the expression runtime package.
View Original GitHub DescriptionFact Check
Summary
The new expression engine's tournament error handler E() had different behavior than the legacy engine, causing many tests in the VM expression E2E nightly job to fail.
For context, tournament wraps every expression with try { return ...; } catch(e) { E(e, this); }. The legacy E() handler (set via setErrorHandler in expression.ts) re-throws ExpressionError and ExpressionExtensionError and silently swallows everything else. The VM E() handler was re-throwing all non-TypeError errors, causing expressions that fail during paired-item resolution to crash in VM mode, where those silently return undefined in legacy mode.
Importantly, this means that paired-item resolution in this context (sub-nodes referencing main-flow nodes across AI connections) is apparently not supported. The legacy engine masks this by swallowing the error. Unclear if this is intentionally unsupported or an oversight.
[!note] This fixes many but not all e2e issues.
Related Linear tickets, Github issues, and Community forum posts
https://linear.app/n8n/issue/CAT-2775
Review / Merge checklist
- PR title and summary are descriptive. (conventions) <!-- **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** -->
- Docs updated or follow-up ticket created.
- Tests included. <!-- A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. -->
- PR Labeled with
Backport to Beta,Backport to Stable, orBackport to v1(if the PR is an urgent fix that needs to be backported)