mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix: Overhaul expression error messages related to paired item (#8765)
This commit is contained in:
@@ -271,9 +271,11 @@ export class WorkflowDataProxy {
|
||||
}
|
||||
|
||||
if (!that.workflow.getNode(nodeName)) {
|
||||
throw new ExpressionError(`"${nodeName}" node doesn't exist`, {
|
||||
throw new ExpressionError("Referenced node doesn't exist", {
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
nodeCause: nodeName,
|
||||
descriptionKey: 'nodeNotFound',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -281,10 +283,11 @@ export class WorkflowDataProxy {
|
||||
!that.runExecutionData.resultData.runData.hasOwnProperty(nodeName) &&
|
||||
!that.workflow.getPinDataOfNode(nodeName)
|
||||
) {
|
||||
throw new ExpressionError(`no data, execute "${nodeName}" node first`, {
|
||||
throw new ExpressionError('Referenced node is unexecuted', {
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
type: 'no_node_execution_data',
|
||||
descriptionKey: 'noNodeExecutionData',
|
||||
nodeCause: nodeName,
|
||||
});
|
||||
}
|
||||
@@ -369,7 +372,12 @@ export class WorkflowDataProxy {
|
||||
name = name.toString();
|
||||
|
||||
if (!node) {
|
||||
throw new ExpressionError(`"${nodeName}" node doesn't exist`);
|
||||
throw new ExpressionError("Referenced node doesn't exist", {
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
nodeCause: nodeName,
|
||||
descriptionKey: 'nodeNotFound',
|
||||
});
|
||||
}
|
||||
|
||||
if (['binary', 'data', 'json'].includes(name)) {
|
||||
@@ -380,8 +388,7 @@ export class WorkflowDataProxy {
|
||||
throw new ExpressionError('No execution data available', {
|
||||
messageTemplate:
|
||||
'No execution data available to expression under ‘%%PARAMETER%%’',
|
||||
description:
|
||||
'This node has no input data. Please make sure this node is connected to another node.',
|
||||
descriptionKey: 'noInputConnection',
|
||||
nodeCause: nodeName,
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
@@ -589,9 +596,11 @@ export class WorkflowDataProxy {
|
||||
const nodeName = name.toString();
|
||||
|
||||
if (that.workflow.getNode(nodeName) === null) {
|
||||
throw new ExpressionError(`"${nodeName}" node doesn't exist`, {
|
||||
throw new ExpressionError("Referenced node doesn't exist", {
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
nodeCause: nodeName,
|
||||
descriptionKey: 'nodeNotFound',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -666,10 +675,9 @@ export class WorkflowDataProxy {
|
||||
if (!context) {
|
||||
context = {};
|
||||
}
|
||||
message = `‘Node ${nodeName}‘ must be unpinned to execute`;
|
||||
message = `Unpin '${nodeName}' to execute`;
|
||||
context.messageTemplate = undefined;
|
||||
context.description = `To fetch the data for the expression, you must unpin the node <strong>'${nodeName}'</strong> and execute the workflow again.`;
|
||||
context.descriptionTemplate = `To fetch the data for the expression under '%%PARAMETER%%', you must unpin the node <strong>'${nodeName}'</strong> and execute the workflow again.`;
|
||||
context.descriptionKey = 'pairedItemPinned';
|
||||
}
|
||||
|
||||
if (context.moreInfoLink && (pinData || isScriptingNode(nodeName, that.workflow))) {
|
||||
@@ -688,6 +696,48 @@ export class WorkflowDataProxy {
|
||||
});
|
||||
};
|
||||
|
||||
const createInvalidPairedItemError = ({ nodeName }: { nodeName: string }) => {
|
||||
return createExpressionError("Can't get data for expression", {
|
||||
messageTemplate: 'Expression info invalid',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
message: "Can't get data",
|
||||
},
|
||||
nodeCause: nodeName,
|
||||
descriptionKey: 'pairedItemInvalidInfo',
|
||||
type: 'paired_item_invalid_info',
|
||||
});
|
||||
};
|
||||
|
||||
const createMissingPairedItemError = (nodeCause: string) => {
|
||||
return createExpressionError("Can't get data for expression", {
|
||||
messageTemplate: 'Info for expression missing from previous node',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
message: "Can't get data",
|
||||
},
|
||||
nodeCause,
|
||||
descriptionKey: isScriptingNode(nodeCause, that.workflow)
|
||||
? 'pairedItemNoInfoCodeNode'
|
||||
: 'pairedItemNoInfo',
|
||||
causeDetailed: `Missing pairedItem data (node '${nodeCause}' probably didn't supply it)`,
|
||||
type: 'paired_item_no_info',
|
||||
});
|
||||
};
|
||||
|
||||
const createNoConnectionError = (nodeCause: string) => {
|
||||
return createExpressionError('Invalid expression', {
|
||||
messageTemplate: 'No path back to referenced node',
|
||||
functionality: 'pairedItem',
|
||||
descriptionKey: isScriptingNode(nodeCause, that.workflow)
|
||||
? 'pairedItemNoConnectionCodeNode'
|
||||
: 'pairedItemNoConnection',
|
||||
type: 'paired_item_no_connection',
|
||||
moreInfoLink: true,
|
||||
nodeCause,
|
||||
});
|
||||
};
|
||||
|
||||
const getPairedItem = (
|
||||
destinationNodeName: string,
|
||||
incomingSourceData: ISourceData | null,
|
||||
@@ -736,42 +786,15 @@ export class WorkflowDataProxy {
|
||||
const source = taskData?.source ?? [];
|
||||
|
||||
if (pairedItem.item >= previousNodeOutputData.length) {
|
||||
throw createExpressionError('Can’t get data for expression', {
|
||||
messageTemplate: 'Can’t get data for expression under ‘%%PARAMETER%%’ field',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
message: 'Can’t get data',
|
||||
},
|
||||
nodeCause: nodeBeforeLast,
|
||||
description: `In node ‘<strong>${nodeBeforeLast!}</strong>’, output item ${
|
||||
currentPairedItem.item || 0
|
||||
} ${
|
||||
sourceData.previousNodeRun
|
||||
? `of run ${(sourceData.previousNodeRun || 0).toString()} `
|
||||
: ''
|
||||
}points to an input item on node ‘<strong>${
|
||||
sourceData.previousNode
|
||||
}</strong>‘ that doesn’t exist.`,
|
||||
type: 'paired_item_invalid_info',
|
||||
moreInfoLink: true,
|
||||
throw createInvalidPairedItemError({
|
||||
nodeName: sourceData.previousNode,
|
||||
});
|
||||
}
|
||||
|
||||
const itemPreviousNode: INodeExecutionData = previousNodeOutputData[pairedItem.item];
|
||||
|
||||
if (itemPreviousNode.pairedItem === undefined) {
|
||||
throw createExpressionError('Can’t get data for expression', {
|
||||
messageTemplate: 'Can’t get data for expression under ‘%%PARAMETER%%’ field',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
message: 'Can’t get data',
|
||||
},
|
||||
nodeCause: sourceData.previousNode,
|
||||
description: `To fetch the data from other nodes that this expression needs, more information is needed from the node ‘<strong>${sourceData.previousNode}</strong>’`,
|
||||
causeDetailed: `Missing pairedItem data (node ‘${sourceData.previousNode}’ probably didn’t supply it)`,
|
||||
type: 'paired_item_no_info',
|
||||
moreInfoLink: true,
|
||||
});
|
||||
throw createMissingPairedItemError(sourceData.previousNode);
|
||||
}
|
||||
|
||||
if (Array.isArray(itemPreviousNode.pairedItem)) {
|
||||
@@ -804,13 +827,17 @@ export class WorkflowDataProxy {
|
||||
}
|
||||
|
||||
throw createExpressionError('Invalid expression', {
|
||||
messageTemplate: 'Invalid expression under ‘%%PARAMETER%%’',
|
||||
messageTemplate: `Multiple matching items for expression [item ${
|
||||
currentPairedItem.item || 0
|
||||
}]`,
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
description: `The code uses data in the node ‘<strong>${destinationNodeName}</strong>’ but there is more than one matching item in that node`,
|
||||
message: 'Invalid code',
|
||||
message: `Multiple matching items for code [item ${currentPairedItem.item || 0}]`,
|
||||
},
|
||||
description: `The expression uses data in the node ‘<strong>${destinationNodeName}</strong>’ but there is more than one matching item in that node`,
|
||||
nodeCause: destinationNodeName,
|
||||
descriptionKey: isScriptingNode(destinationNodeName, that.workflow)
|
||||
? 'pairedItemMultipleMatchesCodeNode'
|
||||
: 'pairedItemMultipleMatches',
|
||||
type: 'paired_item_multiple_matches',
|
||||
});
|
||||
}
|
||||
@@ -833,17 +860,7 @@ export class WorkflowDataProxy {
|
||||
if (itemInput >= source.length) {
|
||||
if (source.length === 0) {
|
||||
// A trigger node got reached, so looks like that that item can not be resolved
|
||||
throw createExpressionError('Invalid expression', {
|
||||
messageTemplate: 'Invalid expression under ‘%%PARAMETER%%’',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
description: `The code uses data in the node ‘<strong>${destinationNodeName}</strong>’ but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
|
||||
message: 'Invalid code',
|
||||
},
|
||||
description: `The expression uses data in the node ‘<strong>${destinationNodeName}</strong>’ but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
|
||||
type: 'paired_item_no_connection',
|
||||
moreInfoLink: true,
|
||||
});
|
||||
throw createNoConnectionError(destinationNodeName);
|
||||
}
|
||||
throw createExpressionError('Can’t get data for expression', {
|
||||
messageTemplate: 'Can’t get data for expression under ‘%%PARAMETER%%’ field',
|
||||
@@ -905,24 +922,8 @@ export class WorkflowDataProxy {
|
||||
}
|
||||
|
||||
if (pairedItem.item >= taskData.data!.main[previousNodeOutput]!.length) {
|
||||
throw createExpressionError('Can’t get data for expression', {
|
||||
messageTemplate: 'Can’t get data for expression under ‘%%PARAMETER%%’ field',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
message: 'Can’t get data',
|
||||
},
|
||||
nodeCause: nodeBeforeLast,
|
||||
description: `In node ‘<strong>${nodeBeforeLast!}</strong>’, output item ${
|
||||
currentPairedItem.item || 0
|
||||
} ${
|
||||
sourceData.previousNodeRun
|
||||
? `of run ${(sourceData.previousNodeRun || 0).toString()} `
|
||||
: ''
|
||||
}points to an input item on node ‘<strong>${
|
||||
sourceData.previousNode
|
||||
}</strong>‘ that doesn’t exist.`,
|
||||
type: 'paired_item_invalid_info',
|
||||
moreInfoLink: true,
|
||||
throw createInvalidPairedItemError({
|
||||
nodeName: sourceData.previousNode,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -937,7 +938,12 @@ export class WorkflowDataProxy {
|
||||
|
||||
const referencedNode = that.workflow.getNode(nodeName);
|
||||
if (referencedNode === null) {
|
||||
throw createExpressionError(`"${nodeName}" node doesn't exist`);
|
||||
throw createExpressionError("Referenced node doesn't exist", {
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
nodeCause: nodeName,
|
||||
descriptionKey: 'nodeNotFound',
|
||||
});
|
||||
}
|
||||
|
||||
const ensureNodeExecutionData = () => {
|
||||
@@ -945,8 +951,11 @@ export class WorkflowDataProxy {
|
||||
!that?.runExecutionData?.resultData?.runData.hasOwnProperty(nodeName) &&
|
||||
!that.workflow.getPinDataOfNode(nodeName)
|
||||
) {
|
||||
throw createExpressionError(`no data, execute "${nodeName}" node first`, {
|
||||
throw createExpressionError('Referenced node is unexecuted', {
|
||||
runIndex: that.runIndex,
|
||||
itemIndex: that.itemIndex,
|
||||
type: 'no_node_execution_data',
|
||||
descriptionKey: 'noNodeExecutionData',
|
||||
nodeCause: nodeName,
|
||||
});
|
||||
}
|
||||
@@ -989,17 +998,7 @@ export class WorkflowDataProxy {
|
||||
}
|
||||
const parentNodes = that.workflow.getParentNodes(contextNode);
|
||||
if (!parentNodes.includes(nodeName)) {
|
||||
throw createExpressionError('Invalid expression', {
|
||||
messageTemplate: 'Invalid expression under ‘%%PARAMETER%%’',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
description: `The code uses data in the node <strong>‘${nodeName}’</strong> but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
|
||||
message: `No path back to node ‘${nodeName}’`,
|
||||
},
|
||||
description: `The expression uses data in the node <strong>‘${nodeName}’</strong> but there is no path back to it. Please check this node is connected to it (there can be other nodes in between).`,
|
||||
nodeCause: nodeName,
|
||||
type: 'paired_item_no_connection',
|
||||
});
|
||||
throw createNoConnectionError(nodeName);
|
||||
}
|
||||
|
||||
ensureNodeExecutionData();
|
||||
@@ -1037,17 +1036,7 @@ export class WorkflowDataProxy {
|
||||
const pairedItem = input.pairedItem as IPairedItemData;
|
||||
|
||||
if (pairedItem === undefined) {
|
||||
throw createExpressionError('Can’t get data for expression', {
|
||||
messageTemplate: 'Can’t get data for expression under ‘%%PARAMETER%%’ field',
|
||||
functionality: 'pairedItem',
|
||||
functionOverrides: {
|
||||
description: `To fetch the data from other nodes that this code needs, more information is needed from the node ‘<strong>${that.activeNodeName}</strong>‘`,
|
||||
message: 'Can’t get data',
|
||||
},
|
||||
description: `To fetch the data from other nodes that this expression needs, more information is needed from the node ‘<strong>${that.activeNodeName}</strong>‘`,
|
||||
causeDetailed: `Missing pairedItem data (node ‘${that.activeNodeName}‘ probably didn’t supply it)`,
|
||||
itemIndex,
|
||||
});
|
||||
throw createMissingPairedItemError(that.activeNodeName);
|
||||
}
|
||||
|
||||
if (!that.executeData?.source) {
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface ExpressionErrorOptions {
|
||||
cause?: Error;
|
||||
causeDetailed?: string;
|
||||
description?: string;
|
||||
descriptionKey?: string;
|
||||
descriptionTemplate?: string;
|
||||
functionality?: 'pairedItem';
|
||||
itemIndex?: number;
|
||||
@@ -38,6 +39,7 @@ export class ExpressionError extends ExecutionBaseError {
|
||||
const allowedKeys = [
|
||||
'causeDetailed',
|
||||
'descriptionTemplate',
|
||||
'descriptionKey',
|
||||
'functionality',
|
||||
'itemIndex',
|
||||
'messageTemplate',
|
||||
|
||||
@@ -162,7 +162,7 @@ describe('WorkflowDataProxy', () => {
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(ExpressionError);
|
||||
const exprError = error as ExpressionError;
|
||||
expect(exprError.message).toEqual('"does not exist" node doesn\'t exist');
|
||||
expect(exprError.message).toEqual("Referenced node doesn't exist");
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -193,7 +193,7 @@ describe('WorkflowDataProxy', () => {
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(ExpressionError);
|
||||
const exprError = error as ExpressionError;
|
||||
expect(exprError.message).toEqual('no data, execute "Impossible" node first');
|
||||
expect(exprError.message).toEqual('Referenced node is unexecuted');
|
||||
expect(exprError.context.type).toEqual('no_node_execution_data');
|
||||
done();
|
||||
}
|
||||
@@ -221,7 +221,7 @@ describe('WorkflowDataProxy', () => {
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(ExpressionError);
|
||||
const exprError = error as ExpressionError;
|
||||
expect(exprError.message).toEqual('no data, execute "Impossible if" node first');
|
||||
expect(exprError.message).toEqual('Referenced node is unexecuted');
|
||||
expect(exprError.context.type).toEqual('no_node_execution_data');
|
||||
done();
|
||||
}
|
||||
@@ -263,7 +263,7 @@ describe('WorkflowDataProxy', () => {
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(ExpressionError);
|
||||
const exprError = error as ExpressionError;
|
||||
expect(exprError.message).toEqual('Can’t get data for expression');
|
||||
expect(exprError.message).toEqual("Can't get data for expression");
|
||||
expect(exprError.context.type).toEqual('paired_item_no_info');
|
||||
done();
|
||||
}
|
||||
@@ -277,7 +277,7 @@ describe('WorkflowDataProxy', () => {
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(ExpressionError);
|
||||
const exprError = error as ExpressionError;
|
||||
expect(exprError.message).toEqual('Can’t get data for expression');
|
||||
expect(exprError.message).toEqual("Can't get data for expression");
|
||||
expect(exprError.context.type).toEqual('paired_item_invalid_info');
|
||||
done();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user