diff --git a/src/errors.ts b/src/errors.ts index b2db275..7f8192a 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -9,6 +9,13 @@ export interface AzFuncError { isAzureFunctionsSystemError: boolean; } +export interface ValidatedError extends Error, Partial { + /** + * Use `trySetErrorMessage` to set the error message + */ + readonly message: string; +} + export class AzFuncSystemError extends Error { isAzureFunctionsSystemError = true; } @@ -27,22 +34,8 @@ export class ReadOnlyError extends AzFuncTypeError { } } -export function ensureErrorType(err: unknown): Error & Partial { +export function ensureErrorType(err: unknown): ValidatedError { if (err instanceof Error) { - const writable = Object.getOwnPropertyDescriptor(err, 'message')?.writable; - if (!writable) { - // The motivation for this branch can be found in the below issue: - // https://github.com/Azure/azure-functions-nodejs-library/issues/205 - let readableMessage = err.message; - Object.defineProperty(err, 'message', { - get() { - return readableMessage; - }, - set(val: string) { - readableMessage = val; - }, - }); - } return err; } else { let message: string; @@ -59,6 +52,14 @@ export function ensureErrorType(err: unknown): Error & Partial { } } +export function trySetErrorMessage(err: Error, message: string): void { + try { + err.message = message; + } catch { + // If we can't set the message, we'll keep the error as is + } +} + /** * This is mostly for callbacks where `null` or `undefined` indicates there is no error * By contrast, anything thrown/caught is assumed to be an error regardless of what it is diff --git a/test/errors.test.ts b/test/errors.test.ts index 67dc2f9..eb65e23 100644 --- a/test/errors.test.ts +++ b/test/errors.test.ts @@ -3,7 +3,7 @@ import 'mocha'; import { expect } from 'chai'; -import { ensureErrorType } from '../src/errors'; +import { ensureErrorType, trySetErrorMessage } from '../src/errors'; describe('ensureErrorType', () => { it('null', () => { @@ -42,6 +42,13 @@ describe('ensureErrorType', () => { expect(ensureErrorType(actualError)).to.equal(actualError); }); + it('modify error message', () => { + const actualError = new Error('test2'); + trySetErrorMessage(actualError, 'modified message'); + + expect(actualError.message).to.equal('modified message'); + }); + it('readonly error', () => { class ReadOnlyError extends Error { get message(): string { @@ -55,10 +62,11 @@ describe('ensureErrorType', () => { expect(() => (actualError.message = 'exception')).to.throw(); const wrappedError = ensureErrorType(actualError); - wrappedError.message = 'Readonly error has been modified'; + const message = 'Readonly error has not been modified'; + trySetErrorMessage(wrappedError, message); - expect(wrappedError.message).to.equal('Readonly error has been modified'); - expect(wrappedError.stack).to.contain('Readonly error has been modified'); + expect(wrappedError.message).to.equal('a readonly message'); + expect(wrappedError.stack).to.not.contain('Readonly error has been modified'); }); function validateError(actual: Error, expectedMessage: string): void {