diff --git a/apps/builder/__tests__/create-first-workspace.test.ts b/apps/builder/__tests__/create-first-workspace.test.ts index aa5f5bf368..bc6db3bcce 100644 --- a/apps/builder/__tests__/create-first-workspace.test.ts +++ b/apps/builder/__tests__/create-first-workspace.test.ts @@ -46,6 +46,7 @@ describe("create-first-workspace", () => { test("recognises only the listed codes, never prototype members", () => { expect(isCreateChannelErrorCode("workspaceLimitReached")).toBe(true) + expect(isCreateChannelErrorCode("sessionExpired")).toBe(true) expect(isCreateChannelErrorCode("toString")).toBe(false) expect(isCreateChannelErrorCode(undefined)).toBe(false) }) diff --git a/apps/builder/__tests__/instagram-facebook-select-page.test.tsx b/apps/builder/__tests__/instagram-facebook-select-page.test.tsx index 1b0102f16a..987c85c2ba 100644 --- a/apps/builder/__tests__/instagram-facebook-select-page.test.tsx +++ b/apps/builder/__tests__/instagram-facebook-select-page.test.tsx @@ -139,7 +139,7 @@ describe("InstagramFacebookSelectPage", () => { mockReadPendingAuth.mockResolvedValue(null) await expect(InstagramFacebookSelectPage()).rejects.toThrow( - "redirect:/channels/create", + "redirect:/channels/create?error=sessionExpired", ) expect(mockGetUserInstagramAccounts).not.toHaveBeenCalled() }) diff --git a/apps/builder/__tests__/instagram-select-page.test.tsx b/apps/builder/__tests__/instagram-select-page.test.tsx index 71cac4d58c..fc34afabd3 100644 --- a/apps/builder/__tests__/instagram-select-page.test.tsx +++ b/apps/builder/__tests__/instagram-select-page.test.tsx @@ -74,7 +74,7 @@ describe("InstagramSelectPage", () => { mockReadPendingAuth.mockResolvedValue(null) await expect(InstagramSelectPage()).rejects.toThrow( - "redirect:/channels/create", + "redirect:/channels/create?error=sessionExpired", ) expect(mockGetInstagramAccount).not.toHaveBeenCalled() }) @@ -85,5 +85,8 @@ describe("InstagramSelectPage", () => { await expect(InstagramSelectPage()).rejects.toThrow( "redirect:/channels/create", ) + expect(mockRedirect).not.toHaveBeenCalledWith( + "/channels/create?error=sessionExpired", + ) }) }) diff --git a/apps/builder/__tests__/messenger-select-page.test.tsx b/apps/builder/__tests__/messenger-select-page.test.tsx index 7e4b29df22..e6f91c7177 100644 --- a/apps/builder/__tests__/messenger-select-page.test.tsx +++ b/apps/builder/__tests__/messenger-select-page.test.tsx @@ -190,7 +190,7 @@ describe("MessengerSelectPage", () => { mockReadPendingAuth.mockResolvedValue(null) await expect(MessengerSelectPage()).rejects.toThrow( - "redirect:/channels/create", + "redirect:/channels/create?error=sessionExpired", ) expect(mockGetUserPages).not.toHaveBeenCalled() }) diff --git a/apps/builder/src/app/(no-sidebar)/channels/instagram-facebook/select/page.tsx b/apps/builder/src/app/(no-sidebar)/channels/instagram-facebook/select/page.tsx index 0f17e5e668..323d224433 100644 --- a/apps/builder/src/app/(no-sidebar)/channels/instagram-facebook/select/page.tsx +++ b/apps/builder/src/app/(no-sidebar)/channels/instagram-facebook/select/page.tsx @@ -80,7 +80,7 @@ export default async function InstagramFacebookSelectPage() { const auth = await readPendingAuth(FB_INSTAGRAM_FACEBOOK_PENDING_AUTH_COOKIE) if (!auth) { - redirect("/channels/create") + redirect("/channels/create?error=sessionExpired") } const accounts = await getUserInstagramAccounts(auth.userToken, auth.version) diff --git a/apps/builder/src/app/(no-sidebar)/channels/instagram/select/page.tsx b/apps/builder/src/app/(no-sidebar)/channels/instagram/select/page.tsx index 78d3fafe65..76a77dd80f 100644 --- a/apps/builder/src/app/(no-sidebar)/channels/instagram/select/page.tsx +++ b/apps/builder/src/app/(no-sidebar)/channels/instagram/select/page.tsx @@ -12,7 +12,7 @@ export default async function InstagramSelectPage() { const auth = await readPendingAuth(FB_INSTAGRAM_PENDING_AUTH_COOKIE) if (!auth) { - redirect("/channels/create") + redirect("/channels/create?error=sessionExpired") } const account = await getInstagramAccount(auth.userToken) diff --git a/apps/builder/src/app/(no-sidebar)/channels/messenger/select/page.tsx b/apps/builder/src/app/(no-sidebar)/channels/messenger/select/page.tsx index c73d61ab28..424817fb15 100644 --- a/apps/builder/src/app/(no-sidebar)/channels/messenger/select/page.tsx +++ b/apps/builder/src/app/(no-sidebar)/channels/messenger/select/page.tsx @@ -58,7 +58,7 @@ export default async function MessengerSelectPage() { const pendingAuth = await readPendingAuth(FB_MESSENGER_PENDING_AUTH_COOKIE) if (!pendingAuth) { - redirect("/channels/create") + redirect("/channels/create?error=sessionExpired") } const { pages, bmLookupFailed } = await getUserPages( diff --git a/apps/builder/src/lib/workspace/create-first-workspace.ts b/apps/builder/src/lib/workspace/create-first-workspace.ts index 499c23ea84..b267864d9d 100644 --- a/apps/builder/src/lib/workspace/create-first-workspace.ts +++ b/apps/builder/src/lib/workspace/create-first-workspace.ts @@ -7,16 +7,15 @@ import type { MessageKey } from "@/features/channel-connect/lib/message-key" const CREATE_CHANNEL_ERROR_PARAM = "error" /** - * Plan-limit failures the "create a workspace for the user's first channel" - * step can raise (`workspaceService.create`'s quota gates), keyed by - * `ChatbotXException.code` → the copy `/channels/create` shows for them. Any - * other error keeps propagating, so an unexpected failure is never dressed - * up as a plan limit. + * Known failures keyed by `ChatbotXException.code` or connect-flow error code + * → the copy `/channels/create` shows for them. Any other error keeps + * propagating, so an unexpected failure is never dressed up as a known error. */ export const CREATE_CHANNEL_ERROR_MESSAGE_KEYS = { workspaceLimitReached: "channels.connectMany.reason.workspaceLimit", trialExpired: "channels.connectMany.sessionError.trialExpired", macLimitReached: "channels.connectMany.sessionError.macLimitReached", + sessionExpired: "channels.connectMany.sessionError.sessionExpired", } as const satisfies Record export type CreateChannelErrorCode =