diff --git a/extensions/elevenlabs/tts.test.ts b/extensions/elevenlabs/tts.test.ts index fe2a32af0c0..5284816e724 100644 --- a/extensions/elevenlabs/tts.test.ts +++ b/extensions/elevenlabs/tts.test.ts @@ -1,5 +1,6 @@ // Elevenlabs tests cover tts plugin behavior. import { MAX_AUDIO_BYTES } from "openclaw/plugin-sdk/media-runtime"; +import { synthesizeElevenLabsLiveSpeech } from "openclaw/plugin-sdk/provider-test-contracts"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createStreamingErrorResponse } from "../test-support/streaming-error-response.js"; import { elevenLabsTTS, elevenLabsTTSStream } from "./tts.js"; @@ -326,3 +327,37 @@ describe("elevenlabs tts diagnostics", () => { ); }); }); + +describe("elevenlabs live audio helper error-path body release", () => { + const originalFetch = globalThis.fetch; + + afterEach(() => { + globalThis.fetch = originalFetch; + }); + + it("cancels an unread streaming error body when ElevenLabs returns non-2xx", async () => { + const cancel = vi.fn(); + const response = new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array([1, 2, 3])); + }, + cancel, + }), + { status: 401 }, + ); + globalThis.fetch = vi.fn(async () => response) as typeof fetch; + + await expect( + synthesizeElevenLabsLiveSpeech({ + text: "OpenClaw leak check.", + apiKey: "x", + outputFormat: "mp3_44100_128", + timeoutMs: 1_000, + }), + ).rejects.toThrow("ElevenLabs live TTS failed (401)"); + + expect(cancel).toHaveBeenCalledOnce(); + expect(response.bodyUsed).toBe(true); + }); +}); diff --git a/src/plugin-sdk/test-helpers/stt-live-audio.ts b/src/plugin-sdk/test-helpers/stt-live-audio.ts index 4bab838995d..c4e92d86546 100644 --- a/src/plugin-sdk/test-helpers/stt-live-audio.ts +++ b/src/plugin-sdk/test-helpers/stt-live-audio.ts @@ -71,6 +71,9 @@ export async function synthesizeElevenLabsLiveSpeech(params: { signal: controller.signal, }); if (!response.ok) { + // Error payloads are not used by the live fixture. Cancel instead of buffering an + // untrusted or unbounded body so Undici can release the connection promptly. + await response.body?.cancel().catch(() => undefined); throw new Error(`ElevenLabs live TTS failed (${response.status})`); } return Buffer.from(await response.arrayBuffer());