




















@@ -16,6 +16,28 @@ vi.mock("openclaw/plugin-sdk/fetch-runtime", async () => {
1616};
1717});
181819+function cancelTrackedResponse(
20+text: string,
21+init: ResponseInit,
22+): {
23+response: Response;
24+wasCanceled: () => boolean;
25+} {
26+let canceled = false;
27+const stream = new ReadableStream<Uint8Array>({
28+start(controller) {
29+controller.enqueue(new TextEncoder().encode(text));
30+},
31+cancel() {
32+canceled = true;
33+},
34+});
35+return {
36+response: new Response(stream, init),
37+wasCanceled: () => canceled,
38+};
39+}
40+1941describe("sendWebhookMessageDiscord proxy support", () => {
2042beforeEach(() => {
2143makeProxyFetchMock.mockReset();
@@ -208,4 +230,39 @@ describe("sendWebhookMessageDiscord proxy support", () => {
208230expect(error.rawBody).toEqual({ message: "upstream unavailable" });
209231globalFetchMock.mockRestore();
210232});
233+234+it("bounds webhook error bodies without using response.text()", async () => {
235+const tracked = cancelTrackedResponse(`${"upstream unavailable ".repeat(1024)}tail`, {
236+status: 503,
237+headers: { "content-type": "text/plain" },
238+});
239+const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
240+const globalFetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(tracked.response);
241+242+const cfg = {
243+channels: {
244+discord: {
245+token: "Bot test-token",
246+},
247+},
248+} as OpenClawConfig;
249+250+const thrown = await sendWebhookMessageDiscord("hello", {
251+ cfg,
252+accountId: "default",
253+webhookId: "123",
254+webhookToken: "abc",
255+wait: true,
256+}).then(
257+() => undefined,
258+(error: unknown) => error,
259+);
260+expect(thrown).toBeInstanceOf(DiscordError);
261+const error = thrown as DiscordError;
262+expect(error.message).toContain("upstream unavailable");
263+expect(JSON.stringify(error.rawBody)).not.toContain("tail");
264+expect(tracked.wasCanceled()).toBe(true);
265+expect(textSpy).not.toHaveBeenCalled();
266+globalFetchMock.mockRestore();
267+});
211268});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。