fix(slack): remove message.channels/message.groups handlers that crash Bolt 4.6 (#32033)
* fix(slack): remove message.channels/message.groups handlers that crash Bolt 4.6 Bolt 4.6 rejects app.event() calls with event names starting with "message." (e.g. "message.channels", "message.groups"), throwing AppInitializationError on startup. These handlers were added in #31701 based on the incorrect assumption that Slack dispatches typed event names to Bolt. In reality, Slack always delivers events with type:"message" regardless of the Event Subscription name; the channel_type field distinguishes the source. The generic app.event("message") handler already receives all channel, group, IM, and MPIM messages. The additional typed handlers were unreachable even if Bolt allowed them, since no event payload ever carries type:"message.channels". This preserves the handleIncomingMessageEvent refactor from #31701 (extracting the handler into a named function) while removing only the broken registrations. Fixes the Slack provider crash loop affecting all accounts on @slack/bolt >= 4.6.0. Closes #31674 (original issue was not caused by missing handlers) * fix: document Slack Bolt 4.6 startup handler fix (#32033) (thanks @mahopan) --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -47,6 +47,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Slack/Bolt startup compatibility: remove invalid `message.channels` and `message.groups` event registrations so Slack providers no longer crash on startup with Bolt 4.6+; channel/group traffic continues through the unified `message` handler (`channel_type`). (#32033) Thanks @mahopan.
|
||||
- Sandbox/Docker setup command parsing: accept `agents.*.sandbox.docker.setupCommand` as either a string or a string array, and normalize arrays to newline-delimited shell scripts so multi-step setup commands no longer concatenate without separators. (#31953) Thanks @liuxiaopai-ai.
|
||||
- Gateway/Plugin HTTP route precedence: run explicit plugin HTTP routes before the Control UI SPA catch-all so registered plugin webhook/custom paths remain reachable, while unmatched paths still fall through to Control UI handling. (#31885) Thanks @Sid-Qin.
|
||||
- Security/Node exec approvals: preserve shell/dispatch-wrapper argv semantics during approval hardening so approved wrapper commands (for example `env sh -c ...`) cannot drift into a different runtime command shape, and add regression coverage for both approval-plan generation and approved runtime execution paths. Thanks @tdjackey for reporting.
|
||||
|
||||
@@ -33,8 +33,6 @@ function createMessageHandlers(overrides?: SlackSystemEventTestOverrides) {
|
||||
});
|
||||
return {
|
||||
handler: harness.getHandler("message") as MessageHandler | null,
|
||||
channelHandler: harness.getHandler("message.channels") as MessageHandler | null,
|
||||
groupHandler: harness.getHandler("message.groups") as MessageHandler | null,
|
||||
handleSlackMessage,
|
||||
};
|
||||
}
|
||||
@@ -159,17 +157,17 @@ describe("registerSlackMessageEvents", () => {
|
||||
expect(messageQueueMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("registers and forwards message.channels and message.groups events", async () => {
|
||||
it("handles channel and group messages via the unified message handler", async () => {
|
||||
messageQueueMock.mockClear();
|
||||
messageAllowMock.mockReset().mockResolvedValue([]);
|
||||
const { channelHandler, groupHandler, handleSlackMessage } = createMessageHandlers({
|
||||
const { handler, handleSlackMessage } = createMessageHandlers({
|
||||
dmPolicy: "open",
|
||||
channelType: "channel",
|
||||
});
|
||||
|
||||
expect(channelHandler).toBeTruthy();
|
||||
expect(groupHandler).toBeTruthy();
|
||||
expect(handler).toBeTruthy();
|
||||
|
||||
// channel_type distinguishes the source; all arrive as event type "message"
|
||||
const channelMessage = {
|
||||
type: "message",
|
||||
channel: "C1",
|
||||
@@ -178,8 +176,8 @@ describe("registerSlackMessageEvents", () => {
|
||||
text: "hello channel",
|
||||
ts: "123.100",
|
||||
};
|
||||
await channelHandler!({ event: channelMessage, body: {} });
|
||||
await groupHandler!({
|
||||
await handler!({ event: channelMessage, body: {} });
|
||||
await handler!({
|
||||
event: {
|
||||
...channelMessage,
|
||||
channel_type: "group",
|
||||
@@ -193,17 +191,19 @@ describe("registerSlackMessageEvents", () => {
|
||||
expect(messageQueueMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("applies subtype system-event handling for message.channels events", async () => {
|
||||
it("applies subtype system-event handling for channel messages", async () => {
|
||||
messageQueueMock.mockClear();
|
||||
messageAllowMock.mockReset().mockResolvedValue([]);
|
||||
const { channelHandler, handleSlackMessage } = createMessageHandlers({
|
||||
const { handler, handleSlackMessage } = createMessageHandlers({
|
||||
dmPolicy: "open",
|
||||
channelType: "channel",
|
||||
});
|
||||
|
||||
expect(channelHandler).toBeTruthy();
|
||||
expect(handler).toBeTruthy();
|
||||
|
||||
await channelHandler!({
|
||||
// message_changed events from channels arrive via the generic "message"
|
||||
// handler with channel_type:"channel" — not a separate event type.
|
||||
await handler!({
|
||||
event: {
|
||||
...makeChangedEvent({ channel: "C1", user: "U1" }),
|
||||
channel_type: "channel",
|
||||
|
||||
@@ -46,23 +46,15 @@ export function registerSlackMessageEvents(params: {
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: Slack Event Subscriptions use names like "message.channels" and
|
||||
// "message.groups" to control *which* message events are delivered, but the
|
||||
// actual event payload always arrives with `type: "message"`. The
|
||||
// `channel_type` field ("channel" | "group" | "im" | "mpim") distinguishes
|
||||
// the source. Bolt rejects `app.event("message.channels")` since v4.6
|
||||
// because it is a subscription label, not a valid event type.
|
||||
ctx.app.event("message", async ({ event, body }: SlackEventMiddlewareArgs<"message">) => {
|
||||
await handleIncomingMessageEvent({ event, body });
|
||||
});
|
||||
// Slack may dispatch channel/group message subscriptions under typed event
|
||||
// names. Register explicit handlers so both delivery styles are supported.
|
||||
ctx.app.event(
|
||||
"message.channels",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"message.channels">) => {
|
||||
await handleIncomingMessageEvent({ event, body });
|
||||
},
|
||||
);
|
||||
ctx.app.event(
|
||||
"message.groups",
|
||||
async ({ event, body }: SlackEventMiddlewareArgs<"message.groups">) => {
|
||||
await handleIncomingMessageEvent({ event, body });
|
||||
},
|
||||
);
|
||||
|
||||
ctx.app.event("app_mention", async ({ event, body }: SlackEventMiddlewareArgs<"app_mention">) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user