fix(editor): Revert changes from PRs #16059, #17959, #17759, and #17585 (#18382)

This commit is contained in:
Raúl Gómez Morales
2025-08-15 11:46:09 +02:00
committed by GitHub
parent e1d8eaa170
commit 62f4361f46
11 changed files with 83 additions and 2517 deletions

View File

@@ -8,13 +8,7 @@ import { DateTime, Duration, Interval, Settings } from 'luxon';
import { augmentArray, augmentObject } from './augment-object';
import { AGENT_LANGCHAIN_NODE_TYPE, SCRIPTING_NODE_TYPES } from './constants';
import { ApplicationError } from '@n8n/errors';
import {
ExpressionError,
type ExpressionErrorOptions,
EXPRESSION_ERROR_MESSAGES,
EXPRESSION_ERROR_TYPES,
EXPRESSION_DESCRIPTION_KEYS,
} from './errors/expression.error';
import { ExpressionError, type ExpressionErrorOptions } from './errors/expression.error';
import { getGlobalState } from './global-state';
import { NodeConnectionTypes } from './interfaces';
import type {
@@ -396,13 +390,11 @@ export class WorkflowDataProxy {
}
if (!that.workflow.getNode(nodeName)) {
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NODE_NOT_FOUND, {
messageTemplate: EXPRESSION_ERROR_MESSAGES.NODE_REFERENCE_TEMPLATE,
throw new ExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: EXPRESSION_DESCRIPTION_KEYS.NODE_NOT_FOUND,
type: EXPRESSION_ERROR_TYPES.PAIRED_ITEM_NO_CONNECTION,
descriptionKey: 'nodeNotFound',
});
}
@@ -410,12 +402,11 @@ export class WorkflowDataProxy {
!that.runExecutionData.resultData.runData.hasOwnProperty(nodeName) &&
!getPinDataIfManualExecution(that.workflow, nodeName, that.mode)
) {
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NODE_NOT_FOUND, {
messageTemplate: EXPRESSION_ERROR_MESSAGES.NODE_REFERENCE_TEMPLATE,
throw new ExpressionError('Referenced node is unexecuted', {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
type: EXPRESSION_ERROR_TYPES.PAIRED_ITEM_NO_CONNECTION,
descriptionKey: EXPRESSION_DESCRIPTION_KEYS.NO_NODE_EXECUTION_DATA,
type: 'no_node_execution_data',
descriptionKey: 'noNodeExecutionData',
nodeCause: nodeName,
});
}
@@ -505,16 +496,11 @@ export class WorkflowDataProxy {
name = name.toString();
if (!node) {
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NODE_NOT_FOUND, {
messageTemplate: EXPRESSION_ERROR_MESSAGES.NODE_REFERENCE_TEMPLATE,
functionality: 'pairedItem',
descriptionKey: isScriptingNode(nodeName, that.workflow)
? EXPRESSION_DESCRIPTION_KEYS.PAIRED_ITEM_NO_CONNECTION_CODE_NODE
: EXPRESSION_DESCRIPTION_KEYS.PAIRED_ITEM_NO_CONNECTION,
type: EXPRESSION_ERROR_TYPES.PAIRED_ITEM_NO_CONNECTION,
nodeCause: nodeName,
throw new ExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
@@ -530,7 +516,7 @@ export class WorkflowDataProxy {
if (executionData.length === 0) {
if (that.workflow.getParentNodes(nodeName).length === 0) {
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NO_EXECUTION_DATA, {
throw new ExpressionError('No execution data available', {
messageTemplate:
'No execution data available to expression under %%PARAMETER%%',
descriptionKey: 'noInputConnection',
@@ -541,7 +527,7 @@ export class WorkflowDataProxy {
});
}
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NO_EXECUTION_DATA, {
throw new ExpressionError('No execution data available', {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
type: 'no_execution_data',
@@ -707,16 +693,11 @@ export class WorkflowDataProxy {
const nodeName = name.toString();
if (that.workflow.getNode(nodeName) === null) {
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NODE_NOT_FOUND, {
messageTemplate: EXPRESSION_ERROR_MESSAGES.NODE_REFERENCE_TEMPLATE,
functionality: 'pairedItem',
descriptionKey: isScriptingNode(nodeName, that.workflow)
? EXPRESSION_DESCRIPTION_KEYS.PAIRED_ITEM_NO_CONNECTION_CODE_NODE
: EXPRESSION_DESCRIPTION_KEYS.PAIRED_ITEM_NO_CONNECTION,
type: EXPRESSION_ERROR_TYPES.PAIRED_ITEM_NO_CONNECTION,
nodeCause: nodeName,
throw new ExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
@@ -833,14 +814,14 @@ export class WorkflowDataProxy {
});
};
const createNodeReferenceError = (nodeCause: string) => {
return createExpressionError(EXPRESSION_ERROR_MESSAGES.NODE_NOT_FOUND, {
messageTemplate: EXPRESSION_ERROR_MESSAGES.NODE_REFERENCE_TEMPLATE,
const createNoConnectionError = (nodeCause: string) => {
return createExpressionError('Invalid expression', {
messageTemplate: 'No path back to referenced node',
functionality: 'pairedItem',
descriptionKey: isScriptingNode(nodeCause, that.workflow)
? EXPRESSION_DESCRIPTION_KEYS.PAIRED_ITEM_NO_CONNECTION_CODE_NODE
: EXPRESSION_DESCRIPTION_KEYS.PAIRED_ITEM_NO_CONNECTION,
type: EXPRESSION_ERROR_TYPES.PAIRED_ITEM_NO_CONNECTION,
? 'pairedItemNoConnectionCodeNode'
: 'pairedItemNoConnection',
type: 'paired_item_no_connection',
moreInfoLink: true,
nodeCause,
});
@@ -1009,7 +990,7 @@ export class WorkflowDataProxy {
const matchedItems = results.filter((result) => result.ok).map((result) => result.result);
if (matchedItems.length === 0) {
if (sourceArray.length === 0) throw createNodeReferenceError(destinationNodeName);
if (sourceArray.length === 0) throw createNoConnectionError(destinationNodeName);
throw createBranchNotFoundError(sourceData.previousNode, pairedItem.item, nodeBeforeLast);
}
@@ -1050,7 +1031,7 @@ export class WorkflowDataProxy {
inputData?.[NodeConnectionTypes.AiTool]?.[0]?.[itemIndex].json;
if (!placeholdersDataInputData) {
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NO_EXECUTION_DATA, {
throw new ExpressionError('No execution data available', {
runIndex,
itemIndex,
type: 'no_execution_data',
@@ -1072,7 +1053,12 @@ export class WorkflowDataProxy {
const referencedNode = that.workflow.getNode(nodeName);
if (referencedNode === null) {
throw createNodeReferenceError(nodeName);
throw createExpressionError("Referenced node doesn't exist", {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
nodeCause: nodeName,
descriptionKey: 'nodeNotFound',
});
}
const ensureNodeExecutionData = () => {
@@ -1080,38 +1066,16 @@ export class WorkflowDataProxy {
!that?.runExecutionData?.resultData?.runData.hasOwnProperty(nodeName) &&
!getPinDataIfManualExecution(that.workflow, nodeName, that.mode)
) {
// Always show helpful "Execute node for preview" message
throw new ExpressionError(EXPRESSION_ERROR_MESSAGES.NO_EXECUTION_DATA, {
messageTemplate: `Execute node "${nodeName}" for preview`,
nodeCause: nodeName,
throw createExpressionError('Referenced node is unexecuted', {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
type: 'no_node_execution_data',
descriptionKey: 'noNodeExecutionData',
nodeCause: nodeName,
});
}
};
const ensureValidPath = () => {
// Check path before execution data
const referencedNode = that.workflow.getNode(nodeName);
if (!referencedNode) {
throw createNodeReferenceError(nodeName);
}
const activeNode = that.workflow.getNode(that.activeNodeName);
let contextNode = that.contextNodeName;
if (activeNode) {
const parentMainInputNode = that.workflow.getParentMainInputNode(activeNode);
contextNode = parentMainInputNode?.name ?? contextNode;
}
// For .first(), .last(), .all() methods, use unidirectional path checking
// (forward only) to maintain traditional paired item behavior
const hasForwardPath = that.workflow.getChildNodes(nodeName).includes(contextNode);
if (!hasForwardPath) {
throw createNodeReferenceError(nodeName);
}
};
return new Proxy(
{},
{
@@ -1144,24 +1108,17 @@ export class WorkflowDataProxy {
property === PAIRED_ITEM_METHOD.ITEM
) {
// Before resolving the pairedItem make sure that the requested node comes in the
// graph before the current one or exists in the workflow
const referencedNode = that.workflow.getNode(nodeName);
if (!referencedNode) {
// Node doesn't exist in the workflow (could be trimmed manual execution)
throw createNodeReferenceError(nodeName);
}
// graph before the current one
const activeNode = that.workflow.getNode(that.activeNodeName);
let contextNode = that.contextNodeName;
if (activeNode) {
const parentMainInputNode = that.workflow.getParentMainInputNode(activeNode);
contextNode = parentMainInputNode?.name ?? contextNode;
contextNode = parentMainInputNode.name ?? contextNode;
}
// Use bidirectional path checking to handle AI/tool nodes properly
if (!that.workflow.hasPath(nodeName, contextNode)) {
throw createNodeReferenceError(nodeName);
const parentNodes = that.workflow.getParentNodes(contextNode);
if (!parentNodes.includes(nodeName)) {
throw createNoConnectionError(nodeName);
}
ensureNodeExecutionData();
@@ -1242,7 +1199,6 @@ export class WorkflowDataProxy {
}
if (property === 'first') {
ensureValidPath();
ensureNodeExecutionData();
return (branchIndex?: number, runIndex?: number) => {
branchIndex =
@@ -1261,7 +1217,6 @@ export class WorkflowDataProxy {
};
}
if (property === 'last') {
ensureValidPath();
ensureNodeExecutionData();
return (branchIndex?: number, runIndex?: number) => {
branchIndex =
@@ -1283,7 +1238,6 @@ export class WorkflowDataProxy {
};
}
if (property === 'all') {
ensureValidPath();
ensureNodeExecutionData();
return (branchIndex?: number, runIndex?: number) => {
branchIndex =
@@ -1322,7 +1276,7 @@ export class WorkflowDataProxy {
if (property === 'isProxy') return true;
if (that.connectionInputData.length === 0) {
throw createExpressionError(EXPRESSION_ERROR_MESSAGES.NO_EXECUTION_DATA, {
throw createExpressionError('No execution data available', {
runIndex: that.runIndex,
itemIndex: that.itemIndex,
type: 'no_execution_data',