From af858dff8ed8cf84ffe2f3d7c7b0037f87409b17 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 20 Aug 2026 12:29:38 -0700 Subject: [PATCH] fix(clickclack): clarify command menu permission errors (#126802) --- extensions/clickclack/src/command-menu.test.ts | 2 ++ extensions/clickclack/src/command-menu.ts | 10 +++++++--- extensions/clickclack/src/gateway.test.ts | 16 +++++++++++----- extensions/clickclack/src/gateway.ts | 7 ++++++- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/extensions/clickclack/src/command-menu.test.ts b/extensions/clickclack/src/command-menu.test.ts index bff77d393d4e..235d0c1b29cb 100644 --- a/extensions/clickclack/src/command-menu.test.ts +++ b/extensions/clickclack/src/command-menu.test.ts @@ -39,6 +39,7 @@ async function syncNativeCommands( mocks.listNativeCommandSpecsForConfig.mockReturnValue(specs); await syncClickClackCommandMenu({ + accountId: "default", cfg: {} as CoreConfig, client: { setBotCommands } as unknown as ReturnType, log, @@ -130,6 +131,7 @@ describe("ClickClack command menu", () => { ]); await syncClickClackCommandMenu({ + accountId: "default", cfg, client: { setBotCommands } as unknown as ReturnType, }); diff --git a/extensions/clickclack/src/command-menu.ts b/extensions/clickclack/src/command-menu.ts index e451c1076421..9c9975463ec5 100644 --- a/extensions/clickclack/src/command-menu.ts +++ b/extensions/clickclack/src/command-menu.ts @@ -86,6 +86,7 @@ function mapNativeCommandSpecsToClickClackMenu( } export async function syncClickClackCommandMenu(params: { + accountId: string; cfg: CoreConfig; client: ReturnType; log?: ClickClackCommandMenuLogger; @@ -98,16 +99,19 @@ export async function syncClickClackCommandMenu(params: { await params.client.setBotCommands(commands); } catch (error) { const status = errorStatus(error); + const messagePrefix = `[${params.accountId}] ClickClack command menu sync`; if (status === 403) { - params.log?.warn?.("ClickClack command menu sync skipped: bot token lacks commands:write"); + params.log?.warn?.( + `${messagePrefix} skipped: ${formatErrorMessage(error)}; verify token/workspace command permissions or set commandMenu: false if menus are not needed`, + ); return; } if (status === 404) { params.log?.debug?.( - "ClickClack command menu sync skipped: server does not support /api/bots/self/commands", + `${messagePrefix} skipped: server does not support /api/bots/self/commands`, ); return; } - params.log?.warn?.(`ClickClack command menu sync failed: ${formatErrorMessage(error)}`); + params.log?.warn?.(`${messagePrefix} failed: ${formatErrorMessage(error)}`); } } diff --git a/extensions/clickclack/src/gateway.test.ts b/extensions/clickclack/src/gateway.test.ts index 42ec2eebea65..79f135da2a7f 100644 --- a/extensions/clickclack/src/gateway.test.ts +++ b/extensions/clickclack/src/gateway.test.ts @@ -61,6 +61,7 @@ vi.mock("./resolve.js", () => ({ })); import { startClickClackGatewayAccount } from "./gateway.js"; +import { ClickClackHttpError } from "./http-client.js"; function createGatewayContext( abortSignal: AbortSignal, @@ -247,23 +248,28 @@ describe("ClickClack gateway", () => { it.each([ { - label: "missing command scope", - error: { status: 403 }, + label: "workspace command permission rejection", + error: new ClickClackHttpError( + 403, + "workspace role no longer permits command updates", + new Headers(), + ), level: "warn" as const, - message: "ClickClack command menu sync skipped: bot token lacks commands:write", + message: + "[default] ClickClack command menu sync skipped: ClickClack 403: workspace role no longer permits command updates; verify token/workspace command permissions or set commandMenu: false if menus are not needed", }, { label: "older server", error: { status: 404 }, level: "debug" as const, message: - "ClickClack command menu sync skipped: server does not support /api/bots/self/commands", + "[default] ClickClack command menu sync skipped: server does not support /api/bots/self/commands", }, { label: "network failure", error: new Error("network unavailable"), level: "warn" as const, - message: "ClickClack command menu sync failed: network unavailable", + message: "[default] ClickClack command menu sync failed: network unavailable", }, ])("continues startup after $label", async ({ error, level, message }) => { mocks.client.setBotCommands.mockRejectedValueOnce(error); diff --git a/extensions/clickclack/src/gateway.ts b/extensions/clickclack/src/gateway.ts index f82a19be6e26..c55cfa8eb540 100644 --- a/extensions/clickclack/src/gateway.ts +++ b/extensions/clickclack/src/gateway.ts @@ -191,7 +191,12 @@ export async function startClickClackGatewayAccount( log: ctx.log, }); if (account.commandMenu) { - await syncClickClackCommandMenu({ cfg: ctx.cfg, client, log: ctx.log }); + await syncClickClackCommandMenu({ + cfg: ctx.cfg, + client, + log: ctx.log, + accountId: account.accountId, + }); } ctx.setStatus({ accountId: account.accountId,