diff --git a/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts b/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts index 15cea1fa9f..e2edbe41e0 100644 --- a/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts +++ b/packages/nodes-base/nodes/Mailchimp/MailchimpTrigger.node.ts @@ -196,7 +196,12 @@ export class MailchimpTrigger implements INodeType { try { await mailchimpApiRequest.call(this, endpoint, 'GET'); } catch (error) { - if (error instanceof NodeApiError && error.cause && 'isAxiosError' in error.cause) { + if ( + error instanceof NodeApiError && + error.cause && + 'isAxiosError' in error.cause && + 'statusCode' in error.cause + ) { if (error.cause.statusCode === 404) { return false; } diff --git a/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts b/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts index 537ce4101b..3378c73699 100644 --- a/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts +++ b/packages/nodes-base/nodes/StopAndError/StopAndError.node.ts @@ -3,6 +3,7 @@ import type { INodeExecutionData, INodeType, INodeTypeDescription, + JsonObject, } from 'n8n-workflow'; import { jsonParse, NodeOperationError } from 'n8n-workflow'; @@ -81,14 +82,14 @@ export class StopAndError implements INodeType { const errorType = this.getNodeParameter('errorType', 0) as 'errorMessage' | 'errorObject'; const { id: workflowId, name: workflowName } = this.getWorkflow(); - let toThrow: string | { name: string; message: string; [otherKey: string]: unknown }; + let toThrow: string | JsonObject; if (errorType === 'errorMessage') { toThrow = this.getNodeParameter('errorMessage', 0) as string; } else { const json = this.getNodeParameter('errorObject', 0) as string; - const errorObject = jsonParse(json); + const errorObject = jsonParse(json); toThrow = { name: 'User-thrown error', diff --git a/packages/nodes-base/nodes/StopAndError/test/node/StopAndError.test.ts b/packages/nodes-base/nodes/StopAndError/test/node/StopAndError.test.ts index 830a50472a..dc0c630de6 100644 --- a/packages/nodes-base/nodes/StopAndError/test/node/StopAndError.test.ts +++ b/packages/nodes-base/nodes/StopAndError/test/node/StopAndError.test.ts @@ -88,7 +88,7 @@ describe('Execute Stop and Error Node', () => { const stopAndError1RunData = result.data.resultData.runData['Stop and Error1']; const stopAndError1Object = ( (stopAndError1RunData as unknown as IDataObject[])[0].error as IDataObject - ).cause; + ).errorResponse; expect(stopAndError1Object).toEqual({ code: 404, diff --git a/packages/workflow/src/errors/abstract/execution-base.error.ts b/packages/workflow/src/errors/abstract/execution-base.error.ts index e963948da6..f33db17311 100644 --- a/packages/workflow/src/errors/abstract/execution-base.error.ts +++ b/packages/workflow/src/errors/abstract/execution-base.error.ts @@ -2,16 +2,16 @@ import type { Functionality, IDataObject, JsonObject, Severity } from '../../Int import { ApplicationError } from '../application.error'; interface ExecutionBaseErrorOptions { - cause?: Error | JsonObject; + cause?: Error; + errorResponse?: JsonObject; } export abstract class ExecutionBaseError extends ApplicationError { description: string | null | undefined; - /** - * @tech_debt Ensure `cause` can only be `Error` or `undefined` - */ - cause: Error | JsonObject | undefined; + cause?: Error; + + errorResponse?: JsonObject; timestamp: number; @@ -23,7 +23,7 @@ export abstract class ExecutionBaseError extends ApplicationError { functionality: Functionality = 'regular'; - constructor(message: string, { cause }: ExecutionBaseErrorOptions) { + constructor(message: string, { cause, errorResponse }: ExecutionBaseErrorOptions = {}) { const options = cause instanceof Error ? { cause } : {}; super(message, options); @@ -35,6 +35,8 @@ export abstract class ExecutionBaseError extends ApplicationError { } else if (cause && !(cause instanceof Error)) { this.cause = cause; } + + if (errorResponse) this.errorResponse = errorResponse; } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/workflow/src/errors/abstract/node.error.ts b/packages/workflow/src/errors/abstract/node.error.ts index 4ef3e3d201..6b22e71ada 100644 --- a/packages/workflow/src/errors/abstract/node.error.ts +++ b/packages/workflow/src/errors/abstract/node.error.ts @@ -38,8 +38,12 @@ export abstract class NodeError extends ExecutionBaseError { node: INode; constructor(node: INode, error: Error | JsonObject) { - const message = error instanceof Error ? error.message : ''; - super(message, { cause: error }); + if (error instanceof Error) { + super(error.message, { cause: error }); + } else { + super('', { errorResponse: error }); + } + this.node = node; } diff --git a/packages/workflow/src/errors/node-api.error.ts b/packages/workflow/src/errors/node-api.error.ts index e46453c5c8..ef57ff3e00 100644 --- a/packages/workflow/src/errors/node-api.error.ts +++ b/packages/workflow/src/errors/node-api.error.ts @@ -112,7 +112,7 @@ export class NodeApiError extends NodeError { constructor( node: INode, - error: JsonObject, + errorResponse: JsonObject, { message, description, @@ -125,38 +125,44 @@ export class NodeApiError extends NodeError { messageMapping, }: NodeApiErrorOptions = {}, ) { - super(node, error); + super(node, errorResponse); // only for request library error - if (error.error) { - removeCircularRefs(error.error as JsonObject); + if (errorResponse.error) { + removeCircularRefs(errorResponse.error as JsonObject); } // if not description provided, try to find it in the error object // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (!description && (error.description || (error?.reason as IDataObject)?.description)) { + if ( + !description && + (errorResponse.description || (errorResponse?.reason as IDataObject)?.description) + ) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - this.description = (error.description || - (error?.reason as IDataObject)?.description) as string; + this.description = (errorResponse.description || + (errorResponse?.reason as IDataObject)?.description) as string; } // if not message provided, try to find it in the error object or set description as message // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (!message && (error.message || (error?.reason as IDataObject)?.message || description)) { + if ( + !message && + (errorResponse.message || (errorResponse?.reason as IDataObject)?.message || description) + ) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - this.message = (error.message || + this.message = (errorResponse.message || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (error?.reason as IDataObject)?.message || + (errorResponse?.reason as IDataObject)?.message || description) as string; } // if it's an error generated by axios // look for descriptions in the response object - if (error.reason) { - const reason: IDataObject = error.reason as unknown as IDataObject; + if (errorResponse.reason) { + const reason: IDataObject = errorResponse.reason as unknown as IDataObject; if (reason.isAxiosError && reason.response) { - error = reason.response as JsonObject; + errorResponse = reason.response as JsonObject; } } @@ -165,7 +171,7 @@ export class NodeApiError extends NodeError { this.httpCode = httpCode; } else { this.httpCode = - this.findProperty(error, ERROR_STATUS_PROPERTIES, ERROR_NESTING_PROPERTIES) ?? null; + this.findProperty(errorResponse, ERROR_STATUS_PROPERTIES, ERROR_NESTING_PROPERTIES) ?? null; } if (severity) { @@ -181,10 +187,10 @@ export class NodeApiError extends NodeError { if (!this.description) { if (parseXml) { - this.setDescriptionFromXml(error.error as string); + this.setDescriptionFromXml(errorResponse.error as string); } else { this.description = this.findProperty( - error, + errorResponse, ERROR_MESSAGE_PROPERTIES, ERROR_NESTING_PROPERTIES, ); @@ -209,8 +215,8 @@ export class NodeApiError extends NodeError { this.description, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing this.httpCode || - (error?.code as string) || - ((error?.reason as JsonObject)?.code as string) || + (errorResponse?.code as string) || + ((errorResponse?.reason as JsonObject)?.code as string) || undefined, messageMapping, ); diff --git a/packages/workflow/src/errors/node-operation.error.ts b/packages/workflow/src/errors/node-operation.error.ts index f4f71115df..719673a913 100644 --- a/packages/workflow/src/errors/node-operation.error.ts +++ b/packages/workflow/src/errors/node-operation.error.ts @@ -1,4 +1,4 @@ -import type { INode } from '..'; +import type { INode, JsonObject } from '..'; import type { NodeOperationErrorOptions } from './node-api.error'; import { NodeError } from './abstract/node.error'; @@ -8,7 +8,11 @@ import { NodeError } from './abstract/node.error'; export class NodeOperationError extends NodeError { lineNumber: number | undefined; - constructor(node: INode, error: Error | string, options: NodeOperationErrorOptions = {}) { + constructor( + node: INode, + error: Error | string | JsonObject, + options: NodeOperationErrorOptions = {}, + ) { if (typeof error === 'string') { error = new Error(error); } diff --git a/packages/workflow/src/errors/subworkflow-operation.error.ts b/packages/workflow/src/errors/subworkflow-operation.error.ts index 3c4e7ca13e..7bf2bf385c 100644 --- a/packages/workflow/src/errors/subworkflow-operation.error.ts +++ b/packages/workflow/src/errors/subworkflow-operation.error.ts @@ -3,7 +3,7 @@ import { WorkflowOperationError } from './workflow-operation.error'; export class SubworkflowOperationError extends WorkflowOperationError { description = ''; - cause: { message: string; stack: string }; + cause: Error; constructor(message: string, description: string) { super(message); @@ -11,6 +11,7 @@ export class SubworkflowOperationError extends WorkflowOperationError { this.description = description; this.cause = { + name: this.name, message, stack: this.stack as string, };