Skip to content

fix(receivers): make AwsLambdaReceiver.toHandler() promise-based (Node.js 24+)#2970

Open
tsushanth wants to merge 1 commit into
slackapi:mainfrom
tsushanth:fix-aws-lambda-promise-handler
Open

fix(receivers): make AwsLambdaReceiver.toHandler() promise-based (Node.js 24+)#2970
tsushanth wants to merge 1 commit into
slackapi:mainfrom
tsushanth:fix-aws-lambda-promise-handler

Conversation

@tsushanth

Copy link
Copy Markdown

Closes #2761.

Summary

AwsLambdaReceiver.toHandler() returns a 3-arg function (event, context, callback) => Promise<AwsResponse>. AWS Lambda's Node.js 24+ runtime inspects the handler's .length at registration and rejects 3-arg handlers with Runtime.CallbackHandlerDeprecated before they're ever invoked, so every Bolt app deployed on the Node 24 runtime crashes at startup.

The callback parameter was never actually invoked anywhere in the receiver — the handler always returned Promise<AwsResponse> directly — so drop it from both the AwsHandler type and the implementation.

-export type AwsHandler = (event: AwsEvent, context: any, callback: AwsCallback) => Promise<AwsResponse>;
+export type AwsHandler = (event: AwsEvent, context: any) => Promise<AwsResponse>;

The existing AwsCallback export is preserved for source compatibility and annotated @deprecated so consumers see the migration path on hover.

Tests

  • All 15 call sites in AwsLambdaReceiver.spec.ts updated from handler(event, {}, (_e, _r) => {}) to handler(event, {}).
  • New regression test pins handler.length === 2 so a future refactor that re-adds a trailing parameter doesn't silently break Node 24 again.

All 488 unit tests pass.

…e.js 24+)

Closes slackapi#2761.

`AwsLambdaReceiver.toHandler()` returned a 3-arg function
`(event, context, callback) => Promise<AwsResponse>`. AWS Lambda's
Node.js 24+ runtime checks the handler's `.length` up front and rejects
3-arg handlers with `Runtime.CallbackHandlerDeprecated` before invoking
it — every Bolt app deployed on the Node 24 runtime crashed at startup.

The callback parameter was never actually invoked anywhere in the
receiver (the handler always returned a `Promise<AwsResponse>` directly),
so drop it from both the `AwsHandler` type and the implementation. The
existing `AwsCallback` export is preserved for source compatibility and
annotated `@deprecated` so consumers see the migration path on hover.

Existing tests are updated to call the handler with two arguments; a
regression test pins `handler.length === 2` so a future refactor that
re-adds a trailing parameter doesn't silently break Node 24 again.
All 488 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tsushanth tsushanth requested a review from a team as a code owner June 11, 2026 15:13
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 976f768

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@salesforce-cla

Copy link
Copy Markdown

Thanks for the contribution! Before we can merge this, we need @tsushanth to sign the Salesforce Inc. Contributor License Agreement.


// biome-ignore lint/suspicious/noExplicitAny: request context can be anything
export type AwsHandler = (event: AwsEvent, context: any, callback: AwsCallback) => Promise<AwsResponse>;
export type AwsHandler = (event: AwsEvent, context: any) => Promise<AwsResponse>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Good call dropping the callback from the exported type rather than just the implementation.

signingSecret: 'my-secret',
logger: noopLogger,
});
const handler = awsReceiver.toHandler();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Thanks for assert.equal(handler.length, 2) to pin the handler, since that is the exact value Lambda runtime reads.

@mwbrooks mwbrooks added bug M-T: confirmed bug report. Issues are confirmed when the reproduction steps are documented semver:patch labels Jun 11, 2026
@mwbrooks mwbrooks added this to the 4.x milestone Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug M-T: confirmed bug report. Issues are confirmed when the reproduction steps are documented cla:missing semver:patch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AwsLambdaReceiver is incompatible with AWS Lambda Node.js 24 runtime (callback handlers deprecated)

2 participants