diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index ed86b4c67bb..eb0cea9ac09 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -284,7 +284,12 @@ jobs: }); isMaintainer = membership?.data?.state === "active"; } catch (error) { - if (error?.status !== 404) { + // GITHUB_TOKEN may not have org/team read perms; treat permission errors as non-fatal. + if (error?.status === 404) { + // ignore + } else if (error?.status === 403) { + core.warning(`Skipping team membership check for ${login}; missing permissions.`); + } else { throw error; } } diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index 805fed383ab..dc5973c75fe 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -300,15 +300,26 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} const allRegistered: import("./slash-commands.js").MattermostRegisteredCommand[] = []; - for (const team of teams) { - const registered = await registerSlashCommands({ + try { + for (const team of teams) { + const registered = await registerSlashCommands({ + client, + teamId: team.id, + callbackUrl, + commands: dedupedCommands, + log: (msg) => runtime.log?.(msg), + }); + allRegistered.push(...registered); + } + } catch (err) { + // If we partially succeeded (some teams had commands created) but later failed, + // clean up the created commands so we don't strand registrations that will 503. + await cleanupSlashCommands({ client, - teamId: team.id, - callbackUrl, - commands: dedupedCommands, + commands: allRegistered, log: (msg) => runtime.log?.(msg), }); - allRegistered.push(...registered); + throw err; } // Build trigger→originalName map for accurate command name resolution diff --git a/src/gateway/server-http.ts b/src/gateway/server-http.ts index 8f605caae43..45056ed8e5a 100644 --- a/src/gateway/server-http.ts +++ b/src/gateway/server-http.ts @@ -119,9 +119,12 @@ function resolveMattermostSlashCallbackPaths( const mmRaw = configSnapshot.channels?.mattermost as Record | undefined; const addMmCommands = (raw: unknown) => { - const commands = raw as Record | undefined; - callbackPaths.add(normalizeCallbackPath(commands?.callbackPath)); - tryAddCallbackUrlPath(commands?.callbackUrl); + if (raw == null || typeof raw !== "object") { + return; + } + const commands = raw as Record; + callbackPaths.add(normalizeCallbackPath(commands.callbackPath)); + tryAddCallbackUrlPath(commands.callbackUrl); }; addMmCommands(mmRaw?.commands);