diff --git a/packages/server/src/gateway/__tests__/slack-platform-bridge.test.ts b/packages/server/src/gateway/__tests__/slack-platform-bridge.test.ts index abe4e92fc..b584c14ca 100644 --- a/packages/server/src/gateway/__tests__/slack-platform-bridge.test.ts +++ b/packages/server/src/gateway/__tests__/slack-platform-bridge.test.ts @@ -334,6 +334,25 @@ describe("Slack platform bridge", () => { expect(view.blocks.some((b) => b.type === "header")).toBe(false); }); + test("falls back to a minimal home view if the rich view is rejected", async () => { + const h = makeHomeChat(); + registerSlackAppHome(h.chat, connection(), {}); + let calls = 0; + const publishHomeView = mock(async () => { + calls += 1; + if (calls === 1) throw new Error("invalid_blocks"); + }); + await h.open("U123", publishHomeView); + expect(publishHomeView).toHaveBeenCalledTimes(2); + const fallback = publishHomeView.mock.calls[1]![1] as { + type: string; + blocks: Array>; + }; + expect(fallback.type).toBe("home"); + expect(fallback.blocks.length).toBe(1); + expect(blocksText(fallback)).toContain("/lobu help"); + }); + test("renders the preview-workspace home tab without touching the MCP config", async () => { const h = makeHomeChat(); const getMcpStatus = mock(async () => mcpStatus); diff --git a/packages/server/src/gateway/connections/slack-platform-bridge.ts b/packages/server/src/gateway/connections/slack-platform-bridge.ts index 1b00c5c0b..12a931006 100644 --- a/packages/server/src/gateway/connections/slack-platform-bridge.ts +++ b/packages/server/src/gateway/connections/slack-platform-bridge.ts @@ -343,6 +343,28 @@ async function loadIntegrationStatusesScoped( return result; } +/** Extract something useful out of a Slack `WebAPIPlatformError` (or anything). */ +function errorDetail(error: unknown): Record { + if (error instanceof Error) { + const data = (error as { data?: unknown }).data; + return { + message: error.message, + ...(data && typeof data === "object" ? { slack: data } : {}), + }; + } + return { message: String(error) }; +} + +const HOME_FALLBACK_BLOCKS: unknown[] = [ + { + type: "section", + text: { + type: "mrkdwn", + text: "*Lobu* :wave:\n\nMention me in any channel, or send me a DM, to start a thread. Use `/lobu help` for the built-in commands.", + }, + }, +]; + async function publishHome( adapter: SlackHomeAdapter | undefined, params: HomeViewParams @@ -355,12 +377,29 @@ async function publishHome( } catch (error) { logger.warn( { - error, + error: errorDetail(error), connectionId: params.connection.id, userId: params.userId, }, - "Failed to publish Slack home tab" + "Failed to publish Slack home tab; falling back to a minimal view" ); + // The rich view failed (likely Block Kit validation). Don't leave the user + // staring at a stale cached view — publish a plain text-only home tab. + try { + await publishHomeView(params.userId, { + type: "home", + blocks: HOME_FALLBACK_BLOCKS, + }); + } catch (fallbackError) { + logger.warn( + { + error: errorDetail(fallbackError), + connectionId: params.connection.id, + userId: params.userId, + }, + "Failed to publish fallback Slack home tab" + ); + } } }