From b609e446541cbf136c39d1dcce9cb2cc0dfe7fa2 Mon Sep 17 00:00:00 2001 From: yetval Date: Wed, 17 Jun 2026 00:36:55 +0000 Subject: [PATCH] fix(whatsapp): keep opening text chunk when first media fails on multi-chunk reply When a WhatsApp auto-reply spans more than one chunk and is sent with an image whose send fails, the first chunk was silently dropped. The first-media onError fallback evaluated remainingText.shift() before the caption, so on a multi-chunk reply the truthy second chunk discarded the caption that held the opening chunk. Resurface the caption directly; the trailing loop already sends the rest. Adds a regression test driving deliverWebReply with a two-chunk reply and a failing first media send. --- .../src/auto-reply/deliver-reply.test.ts | 26 +++++++++++++++++++ .../whatsapp/src/auto-reply/deliver-reply.ts | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts b/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts index 69c444c4ce0..2ee30376145 100644 --- a/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts +++ b/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts @@ -624,6 +624,32 @@ describe("deliverWebReply", () => { expect(warnContext.mediaUrl).toBe("http://example.com/img.jpg"); }); + it("delivers the opening text chunk when the first media fails on a multi-chunk reply", async () => { + const msg = makeMsg(); + mockLoadedImageMedia(); + mockFirstSendMediaFailure(msg, "boom"); + + await deliverWebReply({ + replyResult: { text: "ALPHALINEBRAVOLINE", mediaUrl: "http://example.com/img.jpg" }, + msg, + maxMediaBytes: 1024 * 1024, + textLimit: 9, + replyLogger, + skipLog: true, + }); + + expect(replyText(msg, 0)).toContain("ALPHALINE"); + expect(replyText(msg, 0)).toContain("⚠️ Media failed"); + const allReplies = ( + msg.platform.reply as unknown as { mock: { calls: unknown[][] } } + ).mock.calls + .map((call) => String(call[0])) + .join("\n"); + expect(allReplies).toContain("ALPHALINE"); + expect(allReplies).toContain("BRAVOLINE"); + expect(allReplies).not.toContain("boom"); + }); + it("still attempts later media after the first media fails", async () => { vi.clearAllMocks(); const msg = makeMsg(); diff --git a/extensions/whatsapp/src/auto-reply/deliver-reply.ts b/extensions/whatsapp/src/auto-reply/deliver-reply.ts index 1b92289e2fb..f9f53ff3833 100644 --- a/extensions/whatsapp/src/auto-reply/deliver-reply.ts +++ b/extensions/whatsapp/src/auto-reply/deliver-reply.ts @@ -340,7 +340,7 @@ export async function deliverWebReply(params: { return; } const warning = "⚠️ Media failed."; - const fallbackTextParts = [remainingText.shift() ?? caption ?? "", warning].filter(Boolean); + const fallbackTextParts = [caption ?? "", warning].filter(Boolean); const fallbackText = fallbackTextParts.join("\n"); if (!fallbackText) { return;