Bug type
Regression (worked before, now fails)
Summary
Voice messages delivered via MEDIA:path + [[audio_as_voice]] are sent twice to Telegram, Discord, and WhatsApp. Confirmed on 2026.4.15, not fixed in 4.20.
Root Cause (confirmed via stack traces)
Two independent normalizeMediaPaths() instances fire for the same reply payload:
onBlockReply (streaming handler) creates normalizer instance A with its own persistedMediaBySource cache
buildReplyPayloads (final reply builder) creates normalizer instance B with a separate cache
Same MEDIA path → two UUID outbound files → two channel sends ~190ms apart.
Critically, onBlockReply fires for media content even with blockStreaming: false (the default).
Stack Traces
Send 1 (streaming handler):
dispatch.js:908 → bot:4888 → sendPayload → deliverReplies → sendVoice
Send 2 (buffered block dispatcher):
provider-dispatcher.js:4 → dispatch.js:979 (dispatchInboundMessage)
→ dispatch.js:39 (withReplyDispatcher) → dispatch.js:908
→ bot:4888 → sendPayload → deliverReplies → sendVoice
Steps to reproduce
- Configure Telegram or Discord channel
- Have the agent reply with
MEDIA:/path/to/audio.mp3 + [[audio_as_voice]]
- Observe two identical voice messages delivered
Expected behavior
Single voice message delivery.
Actual behavior
Two voice messages delivered ~190ms apart from the same audio file. Both are audible. Different Telegram message IDs confirm two separate API calls.
Workaround
Use the message tool with action=send + filePath + asVoice=true then reply with NO_REPLY. This bypasses the reply-media normalization pipeline entirely.
Alternatively, using the built-in TTS system (messages.tts) also avoids this since it attaches audio directly to the delivery payload.
Related Issues
This appears to be the same root cause across multiple reports:
Environment
- OpenClaw: 2026.4.15 (041266a)
- OS: Ubuntu 24.04, Azure VM
- Channels: Telegram + Discord (both affected)
- blockStreamingDefault: off (default)
- Node: v22.22.2
Suggested Fix Direction
The normalizeMediaPaths duplication could be resolved by either:
- Sharing a single normalizer cache between the streaming and final reply paths
- Deduplicating media payloads by source path before channel delivery
- Skipping the
onBlockReply media path when blockStreaming is disabled
Bug type
Regression (worked before, now fails)
Summary
Voice messages delivered via
MEDIA:path+[[audio_as_voice]]are sent twice to Telegram, Discord, and WhatsApp. Confirmed on 2026.4.15, not fixed in 4.20.Root Cause (confirmed via stack traces)
Two independent
normalizeMediaPaths()instances fire for the same reply payload:onBlockReply(streaming handler) creates normalizer instance A with its ownpersistedMediaBySourcecachebuildReplyPayloads(final reply builder) creates normalizer instance B with a separate cacheSame MEDIA path → two UUID outbound files → two channel sends ~190ms apart.
Critically,
onBlockReplyfires for media content even withblockStreaming: false(the default).Stack Traces
Steps to reproduce
MEDIA:/path/to/audio.mp3+[[audio_as_voice]]Expected behavior
Single voice message delivery.
Actual behavior
Two voice messages delivered ~190ms apart from the same audio file. Both are audible. Different Telegram message IDs confirm two separate API calls.
Workaround
Use the
messagetool withaction=send+filePath+asVoice=truethen reply withNO_REPLY. This bypasses the reply-media normalization pipeline entirely.Alternatively, using the built-in TTS system (
messages.tts) also avoids this since it attaches audio directly to the delivery payload.Related Issues
This appears to be the same root cause across multiple reports:
Environment
Suggested Fix Direction
The
normalizeMediaPathsduplication could be resolved by either:onBlockReplymedia path whenblockStreamingis disabled