refactor(core): Re-parent ExecutionBaseError to BaseError (no-changelog) (#32920)

Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
This commit is contained in:
Tomi Turtiainen 2026-06-25 15:32:16 +03:00 committed by GitHub
parent 5fc00bf17d
commit 330a8a7ede
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 39 additions and 10 deletions

View File

@ -2,7 +2,8 @@ import type { Logger } from '@n8n/backend-common';
import { QueryFailedError } from '@n8n/typeorm';
import type { ErrorEvent } from '@sentry/core';
import { AxiosError } from 'axios';
import { ApplicationError, BaseError } from 'n8n-workflow';
import { ApplicationError, BaseError, ExpressionError, NodeOperationError } from 'n8n-workflow';
import type { INode } from 'n8n-workflow';
import type { Mock } from 'vitest';
import { mock } from 'vitest-mock-extended';
@ -249,6 +250,32 @@ describe('ErrorReporter', () => {
});
});
});
// `ExecutionBaseError` (the base of NodeApiError/NodeOperationError/ExpressionError)
// extends `BaseError`, so these errors are classified by the BaseError branch.
// Reporting must still be driven by their level, as before the re-parenting.
describe('ExecutionBaseError subclasses', () => {
const node = mock<INode>();
it('should drop warning-level node errors', async () => {
const originalException = new NodeOperationError(node, 'boom');
expect(originalException.level).toBe('warning');
expect(await errorReporter.beforeSend(event, { originalException })).toEqual(null);
});
it('should keep error-level node errors', async () => {
const originalException = new NodeOperationError(node, 'boom', { level: 'error' });
expect(await errorReporter.beforeSend(event, { originalException })).toEqual(event);
});
it('should drop an ExpressionError', async () => {
const originalException = new ExpressionError('bad expression');
expect(await errorReporter.beforeSend(event, { originalException })).toEqual(null);
});
});
});
describe('getEventLoopBlockIntegration', () => {

View File

@ -9,7 +9,7 @@ import type {
INodeType,
} from 'n8n-workflow';
import {
ApplicationError,
BaseError,
NodeConnectionTypes,
UnexpectedError,
createRunExecutionData,
@ -254,7 +254,7 @@ describe('processRunExecutionData', () => {
// eslint-disable-next-line @typescript-eslint/promise-function-async
const execution = () => workflowExecute.processRunExecutionData(workflow);
expect(execution).toThrow(ApplicationError);
expect(execution).toThrow(BaseError);
expect(execution).toThrow(
/^The 'node' node has issues:\n- Parameter "Required Text" is required\.$/,
);

View File

@ -1,5 +1,5 @@
import {
ApplicationError,
NodeOperationError,
type ILoadOptionsFunctions,
type INodeListSearchResult,
} from 'n8n-workflow';
@ -138,7 +138,7 @@ describe('AWS Cognito Functions', () => {
getNode: vi.fn(),
} as unknown as ILoadOptionsFunctions;
await expect(searchGroups.call(mockContext)).rejects.toThrow(ApplicationError);
await expect(searchGroups.call(mockContext)).rejects.toThrow(NodeOperationError);
});
});

View File

@ -1,14 +1,16 @@
import { ApplicationError, type ReportingOptions } from '@n8n/errors';
import { BaseError, type BaseErrorOptions } from '../base/base.error';
import type { Functionality, IDataObject, JsonObject } from '../../interfaces';
interface ExecutionBaseErrorOptions extends ReportingOptions {
interface ExecutionBaseErrorOptions extends BaseErrorOptions {
cause?: Error;
errorResponse?: JsonObject;
}
export abstract class ExecutionBaseError extends ApplicationError {
description: string | null | undefined;
export abstract class ExecutionBaseError extends BaseError {
// `BaseError.description` is readonly, but subclasses (e.g. NodeApiError,
// ExpressionError) reassign it, so redeclare it as writable here. `declare`
// avoids re-initializing the field and clobbering the value set by `super`.
declare description: string | null | undefined;
override cause?: Error;