From 9205438d5494d07ea011edc3c154f8252fc5fe0b Mon Sep 17 00:00:00 2001 From: svozza Date: Thu, 14 May 2026 15:10:10 +0100 Subject: [PATCH 1/4] fix(event-handler): correct InvalidHttpMethodError name InvalidHttpMethodError's constructor set this.name to 'InvalidEventError' (copy-paste from the sibling class above it). Name-based dispatch in ErrorHandlerRegistry.resolve would mis-route the method error to an InvalidEventError handler. Fixes #5251. --- packages/event-handler/src/http/errors.ts | 2 +- .../unit/http/ErrorHandlerRegistry.test.ts | 25 ++++++++++++ .../tests/unit/http/converters.test.ts | 38 +++++++++++++++++++ .../tests/unit/http/errors.test.ts | 31 +++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/event-handler/src/http/errors.ts b/packages/event-handler/src/http/errors.ts index a735b4d806..d241464de5 100644 --- a/packages/event-handler/src/http/errors.ts +++ b/packages/event-handler/src/http/errors.ts @@ -228,7 +228,7 @@ class InvalidEventError extends Error { class InvalidHttpMethodError extends Error { constructor(method: string) { super(`HTTP method ${method} is not supported.`); - this.name = 'InvalidEventError'; + this.name = 'InvalidHttpMethodError'; } } diff --git a/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts b/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts index ffe38c7027..2ddcca1a4b 100644 --- a/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts +++ b/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it } from 'vitest'; import { ErrorHandlerRegistry } from '../../../src/http/ErrorHandlerRegistry.js'; +import { + InvalidEventError, + InvalidHttpMethodError, +} from '../../../src/http/errors.js'; import { HttpStatusCodes } from '../../../src/http/index.js'; import type { HttpStatusCode, @@ -142,6 +146,27 @@ describe('Class: ErrorHandlerRegistry', () => { expect(registry.resolve(new InheritedError('test'))).toBe(specificHandler); }); + it('does not route InvalidHttpMethodError to an InvalidEventError handler via name fallback', () => { + // Regression for https://github.com/aws-powertools/powertools-lambda-typescript/issues/5251. + // The two classes are unrelated by inheritance and have distinct constructors, + // so resolution falls through to the name-based step. With the prior bug + // (InvalidHttpMethodError set this.name = 'InvalidEventError'), the registry + // would mis-route the method error to the event-error handler. + + // Prepare + const registry = new ErrorHandlerRegistry({ logger: console }); + const eventErrorHandler = createErrorHandler(HttpStatusCodes.BAD_REQUEST); + + // Act + registry.register(InvalidEventError, eventErrorHandler); + + // Assess + expect(registry.resolve(new InvalidHttpMethodError('CONNECT'))).toBeNull(); + expect(registry.resolve(new InvalidEventError('bad event'))).toBe( + eventErrorHandler + ); + }); + it('prioritizes instanceof match over name-based matching', () => { // Prepare const registry = new ErrorHandlerRegistry({ logger: console }); diff --git a/packages/event-handler/tests/unit/http/converters.test.ts b/packages/event-handler/tests/unit/http/converters.test.ts index cf673e5e41..4ec64b5a29 100644 --- a/packages/event-handler/tests/unit/http/converters.test.ts +++ b/packages/event-handler/tests/unit/http/converters.test.ts @@ -8,6 +8,7 @@ import { bodyToNodeStream, webHeadersToApiGatewayHeaders, } from '../../../src/http/converters.js'; +import { InvalidHttpMethodError } from '../../../src/http/errors.js'; import { HttpStatusCodes, handlerResultToWebResponse, @@ -773,6 +774,43 @@ describe('Converters', () => { }); }); + describe('proxyEventToWebRequest (unsupported HTTP method)', () => { + it.each([ + { + version: 'V1', + createEvent: () => createTestEvent('/test', 'CONNECT'), + }, + { + version: 'V2', + createEvent: () => createTestEventV2('/test', 'CONNECT'), + }, + { + version: 'ALB', + createEvent: () => createTestALBEvent('/test', 'CONNECT'), + }, + ])('throws InvalidHttpMethodError with the correct name for $version events', ({ + createEvent, + }) => { + // Prepare + const event = createEvent(); + + // Act & Assess + expect(() => proxyEventToWebRequest(event)).toThrow( + InvalidHttpMethodError + ); + + try { + proxyEventToWebRequest(event); + } catch (err) { + expect(err).toBeInstanceOf(InvalidHttpMethodError); + expect((err as Error).name).toBe('InvalidHttpMethodError'); + expect((err as Error).message).toBe( + 'HTTP method CONNECT is not supported.' + ); + } + }); + }); + describe('responseToProxyResult', () => { it('converts basic Response to API Gateway result', async () => { // Prepare diff --git a/packages/event-handler/tests/unit/http/errors.test.ts b/packages/event-handler/tests/unit/http/errors.test.ts index 63e32d9708..188e3ac141 100644 --- a/packages/event-handler/tests/unit/http/errors.test.ts +++ b/packages/event-handler/tests/unit/http/errors.test.ts @@ -1,4 +1,8 @@ import { describe, expect, it } from 'vitest'; +import { + InvalidEventError, + InvalidHttpMethodError, +} from '../../../src/http/errors.js'; import { BadRequestError, ForbiddenError, @@ -379,6 +383,33 @@ describe('HTTP Error Classes', () => { }); }); + describe('Invalid* errors (non-HttpError subclasses)', () => { + it('InvalidEventError carries its own name and message', () => { + const error = new InvalidEventError('bad event'); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(InvalidEventError); + expect(error.name).toBe('InvalidEventError'); + expect(error.message).toBe('bad event'); + }); + + it('InvalidHttpMethodError carries its own name and a method-aware message', () => { + const error = new InvalidHttpMethodError('CONNECT'); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(InvalidHttpMethodError); + expect(error.name).toBe('InvalidHttpMethodError'); + expect(error.message).toBe('HTTP method CONNECT is not supported.'); + }); + + it('InvalidEventError and InvalidHttpMethodError have distinct names', () => { + const eventErr = new InvalidEventError(); + const methodErr = new InvalidHttpMethodError('BREW'); + + expect(eventErr.name).not.toBe(methodErr.name); + }); + }); + describe('ResponseValidationError', () => { it('creates error with correct statusCode', () => { const error = new ResponseValidationError( From afdd8078e0fd4784c756cc162b845d7a625b8f7d Mon Sep 17 00:00:00 2001 From: svozza Date: Thu, 14 May 2026 15:39:20 +0100 Subject: [PATCH 2/4] test(event-handler): drop registry name-fallback regression test Removes the ErrorHandlerRegistry test asserting InvalidHttpMethodError is not routed to an InvalidEventError handler via name-fallback. The errors.test.ts and converters.test.ts coverage already pins the .name fix; this registry-level test is redundant. --- .../unit/http/ErrorHandlerRegistry.test.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts b/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts index 2ddcca1a4b..ffe38c7027 100644 --- a/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts +++ b/packages/event-handler/tests/unit/http/ErrorHandlerRegistry.test.ts @@ -1,9 +1,5 @@ import { describe, expect, it } from 'vitest'; import { ErrorHandlerRegistry } from '../../../src/http/ErrorHandlerRegistry.js'; -import { - InvalidEventError, - InvalidHttpMethodError, -} from '../../../src/http/errors.js'; import { HttpStatusCodes } from '../../../src/http/index.js'; import type { HttpStatusCode, @@ -146,27 +142,6 @@ describe('Class: ErrorHandlerRegistry', () => { expect(registry.resolve(new InheritedError('test'))).toBe(specificHandler); }); - it('does not route InvalidHttpMethodError to an InvalidEventError handler via name fallback', () => { - // Regression for https://github.com/aws-powertools/powertools-lambda-typescript/issues/5251. - // The two classes are unrelated by inheritance and have distinct constructors, - // so resolution falls through to the name-based step. With the prior bug - // (InvalidHttpMethodError set this.name = 'InvalidEventError'), the registry - // would mis-route the method error to the event-error handler. - - // Prepare - const registry = new ErrorHandlerRegistry({ logger: console }); - const eventErrorHandler = createErrorHandler(HttpStatusCodes.BAD_REQUEST); - - // Act - registry.register(InvalidEventError, eventErrorHandler); - - // Assess - expect(registry.resolve(new InvalidHttpMethodError('CONNECT'))).toBeNull(); - expect(registry.resolve(new InvalidEventError('bad event'))).toBe( - eventErrorHandler - ); - }); - it('prioritizes instanceof match over name-based matching', () => { // Prepare const registry = new ErrorHandlerRegistry({ logger: console }); From 79b63ccd9dc03f6e55546efe29cccc7982885e77 Mon Sep 17 00:00:00 2001 From: svozza Date: Fri, 15 May 2026 09:58:03 +0100 Subject: [PATCH 3/4] test(event-handler): drop redundant distinct-names test --- packages/event-handler/tests/unit/http/errors.test.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/event-handler/tests/unit/http/errors.test.ts b/packages/event-handler/tests/unit/http/errors.test.ts index 188e3ac141..fe646b39b5 100644 --- a/packages/event-handler/tests/unit/http/errors.test.ts +++ b/packages/event-handler/tests/unit/http/errors.test.ts @@ -401,13 +401,6 @@ describe('HTTP Error Classes', () => { expect(error.name).toBe('InvalidHttpMethodError'); expect(error.message).toBe('HTTP method CONNECT is not supported.'); }); - - it('InvalidEventError and InvalidHttpMethodError have distinct names', () => { - const eventErr = new InvalidEventError(); - const methodErr = new InvalidHttpMethodError('BREW'); - - expect(eventErr.name).not.toBe(methodErr.name); - }); }); describe('ResponseValidationError', () => { From adc0e70f59db206a6abe967f427fdc30070b9187 Mon Sep 17 00:00:00 2001 From: svozza Date: Fri, 15 May 2026 09:59:40 +0100 Subject: [PATCH 4/4] test(event-handler): assert error.name in custom-message it.each --- packages/event-handler/tests/unit/http/errors.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/event-handler/tests/unit/http/errors.test.ts b/packages/event-handler/tests/unit/http/errors.test.ts index fe646b39b5..293bc0e83f 100644 --- a/packages/event-handler/tests/unit/http/errors.test.ts +++ b/packages/event-handler/tests/unit/http/errors.test.ts @@ -84,6 +84,7 @@ describe('HTTP Error Classes', () => { expect(error.message).toBe(customMessage); expect(error.statusCode).toBe(statusCode); expect(error.errorType).toBe(errorType); + expect(error.name).toBe(errorType); }); describe('toJSON', () => {