From ccca99c4727a657bbca9840536db2b21269ded62 Mon Sep 17 00:00:00 2001 From: Greg Mousseau Date: Sat, 28 Feb 2026 13:44:15 -0500 Subject: [PATCH] fix(android): ignore stale out-of-order agent events in streaming TTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent events arrive on multiple threads concurrently. A stale event with shorter accumulated text was falsely triggering 'text diverged', causing the streaming TTS to restart with a new WebSocket — resulting in multiple simultaneous ElevenLabs connections (2-3 voices) and eventual system TTS fallback when hasReceivedAudio was false. Fix: if sentFullText.startsWith(fullText), the event is stale (we already have this text), not diverged. Accept and ignore it. --- .../ai/openclaw/android/voice/ElevenLabsStreamingTts.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/android/app/src/main/java/ai/openclaw/android/voice/ElevenLabsStreamingTts.kt b/apps/android/app/src/main/java/ai/openclaw/android/voice/ElevenLabsStreamingTts.kt index 76583565b4c..9d4d5abf52c 100644 --- a/apps/android/app/src/main/java/ai/openclaw/android/voice/ElevenLabsStreamingTts.kt +++ b/apps/android/app/src/main/java/ai/openclaw/android/voice/ElevenLabsStreamingTts.kt @@ -200,6 +200,9 @@ class ElevenLabsStreamingTts( private var sentFullText = "" /** + // If we already sent a superset of this text, it's just a stale/out-of-order + // event from a different thread — not a real divergence. Ignore it. + if (sentFullText.startsWith(fullText)) return true * Returns true if text was accepted, false if text diverged (caller should restart). */ @Synchronized @@ -210,6 +213,9 @@ class ElevenLabsStreamingTts( // Detect text replacement: if the new text doesn't start with what we already sent, // the stream has diverged (e.g., tool call interrupted and text was replaced). if (sentFullText.isNotEmpty() && !fullText.startsWith(sentFullText)) { + // If we already sent a superset of this text, it's just a stale/out-of-order + // event from a different thread — not a real divergence. Ignore it. + if (sentFullText.startsWith(fullText)) return true Log.d(TAG, "text diverged — sent='${sentFullText.take(60)}' new='${fullText.take(60)}'") return false }