mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-30 18:01:23 +08:00
fix(core): Construct execution errors correctly when Error.prototype is frozen (#33897)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
3bc2292c78
commit
fffa4233b3
@@ -27,7 +27,13 @@ export abstract class ExecutionBaseError extends BaseError {
|
||||
constructor(message: string, options: ExecutionBaseErrorOptions = {}) {
|
||||
super(message, options);
|
||||
|
||||
this.name = this.constructor.name;
|
||||
// Defined, not assigned: assignment throws when `Error.prototype` is frozen (task runner secure mode)
|
||||
Object.defineProperty(this, 'name', {
|
||||
value: this.constructor.name,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
this.timestamp = Date.now();
|
||||
|
||||
const { cause, errorResponse } = options;
|
||||
|
||||
@@ -24,8 +24,17 @@ export class WorkflowActivationError extends ExecutionBaseError {
|
||||
let error = cause as Error;
|
||||
if (cause instanceof ExecutionBaseError) {
|
||||
error = new Error(cause.message);
|
||||
error.constructor = cause.constructor;
|
||||
error.name = cause.name;
|
||||
// Defined, not assigned: a fresh `Error` has no own `constructor`/`name`, so
|
||||
// assignment throws when `Error.prototype` is frozen (task runner secure mode)
|
||||
Object.defineProperties(error, {
|
||||
constructor: {
|
||||
value: cause.constructor,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
},
|
||||
name: { value: cause.name, writable: true, enumerable: true, configurable: true },
|
||||
});
|
||||
error.stack = cause.stack;
|
||||
}
|
||||
super(message, { cause: error });
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ExpressionError } from '../../src/errors';
|
||||
|
||||
describe('ExecutionBaseError', () => {
|
||||
it('should set name to the concrete class name', () => {
|
||||
const error = new ExpressionError('message');
|
||||
|
||||
expect(error.name).toBe('ExpressionError');
|
||||
});
|
||||
|
||||
describe('with a frozen Error.prototype', () => {
|
||||
const nameDescriptor = Object.getOwnPropertyDescriptor(Error.prototype, 'name')!;
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(Error.prototype, 'name', { ...nameDescriptor, writable: false });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Object.defineProperty(Error.prototype, 'name', nameDescriptor);
|
||||
});
|
||||
|
||||
it('should construct and set name without writing through the prototype', () => {
|
||||
const error = new ExpressionError('Paired item data is unavailable');
|
||||
|
||||
expect(error.name).toBe('ExpressionError');
|
||||
expect(Object.getOwnPropertyDescriptor(error, 'name')?.writable).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WorkflowActivationError } from '../../src/errors';
|
||||
import { WorkflowActivationError, WorkflowOperationError } from '../../src/errors';
|
||||
|
||||
describe('WorkflowActivationError', () => {
|
||||
it('should default to `error` level', () => {
|
||||
@@ -44,4 +44,32 @@ describe('WorkflowActivationError', () => {
|
||||
|
||||
expect(error.description).toBe('Actionable detail');
|
||||
});
|
||||
|
||||
describe('with a frozen Error.prototype', () => {
|
||||
const descriptors = {
|
||||
name: Object.getOwnPropertyDescriptor(Error.prototype, 'name')!,
|
||||
constructor: Object.getOwnPropertyDescriptor(Error.prototype, 'constructor')!,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
for (const [key, descriptor] of Object.entries(descriptors)) {
|
||||
Object.defineProperty(Error.prototype, key, { ...descriptor, writable: false });
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
for (const [key, descriptor] of Object.entries(descriptors)) {
|
||||
Object.defineProperty(Error.prototype, key, descriptor);
|
||||
}
|
||||
});
|
||||
|
||||
it('should wrap an ExecutionBaseError cause without writing through the prototype', () => {
|
||||
const operationCause = new WorkflowOperationError('operation failed');
|
||||
|
||||
const error = new WorkflowActivationError('activation failed', { cause: operationCause });
|
||||
|
||||
expect(error.name).toBe('WorkflowActivationError');
|
||||
expect(error.description).toBe('operation failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user