From e2c4d8bd45bea1a3be3fd12fa9ca0cf29f353f9e Mon Sep 17 00:00:00 2001 From: teconomix Date: Fri, 20 Mar 2026 06:03:16 +0000 Subject: [PATCH] fix(mattermost): clean up streaming preview when no final payload arrives; stop retrying failed initial sends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes: 1. onSettled orphan cleanup (Codex ID=2963834802): add cleanup in the streaming dispatcher's onSettled callback for cases where the reply pipeline produces no final payload — e.g. messaging-tool sends suppressed by agent-runner-payloads.ts, or empty/heartbeat responses. Without this, onPartialReply could create a preview post that is never finalized or deleted. The cleanup mirrors the existing logic in #43041 (P5). 2. Initial-send retry storm (Codex ID=2963834806): call stopPatchInterval() in the sendMessageMattermost catch block, mirroring the existing fix for patchMattermostPost failures. Without this, a failed initial post attempt (missing post permission, DM creation failure, etc.) causes the 200ms interval to retry indefinitely for the rest of the response, flooding the API and gateway logs. --- .../mattermost/src/mattermost/monitor.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index f22bcd7adf1..391ccb890e9 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -1499,6 +1499,10 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} runtime.log?.(`stream-patch started ${streamMessageId}`); } catch (err) { logVerboseMessage(`mattermost stream-patch send failed: ${String(err)}`); + // Stop retrying on initial-send failure (e.g. missing post permission, + // DM-channel creation failure). Without this the interval keeps firing + // every 200 ms and flooding the API/logs for the rest of the response. + stopPatchInterval(); } } else { try { @@ -1696,6 +1700,21 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} dispatcher, onSettled: () => { markDispatchIdle(); + // Clean up any streaming preview that was never finalized — this happens when + // the reply pipeline produces no final payload (e.g. messaging-tool sends that + // are suppressed, or empty/heartbeat responses). Without this, onPartialReply + // can create a Mattermost post that is never deleted or patched with final text. + if (streamMessageId && blockStreamingClient) { + stopPatchInterval(); + const orphanId = streamMessageId; + streamMessageId = null; + pendingPatchText = ""; + lastSentText = ""; + patchSending = false; + void deleteMattermostPost(blockStreamingClient, orphanId).catch(() => { + // Best-effort — the run is already complete. + }); + } }, run: () => core.channel.reply.dispatchReplyFromConfig({