openclaw - ✅(Solved) Fix Bug: message tool asVoice parameter defined in schema but never read in handleSendAction [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#73448Fetched 2026-04-29 06:19:48
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Author
Timeline (top)
commented ×1cross-referenced ×1

The message tool's asVoice parameter is defined in the schema but never read or passed through in handleSendAction, so audio sent with asVoice: true is always delivered as a file attachment instead of a voice message.

Root Cause

In message-action-runner-CkqyWfJZ.js, the handleSendAction function (line ~1024) reads many parameters:

const gifPlayback = readBooleanParam(params, 'gifPlayback') ?? false;
const forceDocument = readBooleanParam(params, 'forceDocument') ?? readBooleanParam(params, 'asDocument') ?? false;
const bestEffort = readBooleanParam(params, 'bestEffort');
const silent = readBooleanParam(params, 'silent');

But there is no readBooleanParam(params, 'asVoice') anywhere in the file. A grep for asVoice\|audioAsVoice\|voice in message-action-runner-CkqyWfJZ.js returns zero matches.

Fix Action

Workaround

Manually pre-transcode audio to OGG Opus before sending. The Feishu plugin's isFeishuNativeVoiceAudio() correctly recognizes .ogg/.opus files as native voice format:

ffmpeg -y -i voice.mp3 -c:a libopus -b:a 64k -application voip -ar 48000 voice.ogg

PR fix notes

PR #73483: fix(message): preserve asVoice on shared sends

Description (problem / solution / changelog)

Problem

message tool send calls could accept asVoice, but the shared send path dropped it before generic outbound delivery. Users sending audio through the shared tool/CLI/runtime path could get a regular file/audio attachment instead of a native voice message on channels that support one.

Root cause

handleSendAction read sibling media flags such as gifPlayback and forceDocument, but never read asVoice or mapped it to downstream audioAsVoice. The core sendMessage and gateway send surfaces also did not preserve audioAsVoice, so lower-level send callers had no pass-through for the same semantic.

Complete fix boundary

  • Describe asVoice in the message tool schema as an audio voice-message hint.
  • Add openclaw message send --as-voice and CLI docs for the same user-visible send semantic.
  • Treat asVoice as a standard shared message action parameter.
  • Map message-tool/CLI asVoice to downstream audioAsVoice in the shared action runner.
  • Preserve audioAsVoice through executeSendAction, sendMessage, gateway send, and generated Swift protocol models.
  • Add regression tests for tool schema, CLI parser/help, message action runner, direct sendMessage delivery, and gateway send delivery.

What intentionally did not change

  • Channel-specific action handlers that already read asVoice/audioAsVoice directly.
  • Telegram adapter behavior covered by #42294, which is a narrower stale adapter PR and not the shared core send boundary fixed here.
  • TTS/auto-reply directive paths that already produce audioAsVoice.
  • Channel media transcoders or compatibility validation.

Tests run

  • node scripts/test-projects.mjs src/agents/tools/message-tool.test.ts src/cli/program/message/register.send.test.ts src/infra/outbound/message-action-runner.core-send.test.ts src/infra/outbound/message.test.ts src/gateway/server-methods/send.test.ts
  • pnpm exec oxfmt --check CHANGELOG.md docs/cli/message.md src/agents/tools/message-tool.ts src/agents/tools/message-tool.test.ts src/cli/program/message/register.send.ts src/cli/program/message/register.send.test.ts src/infra/outbound/message-action-param-keys.ts src/infra/outbound/message-action-runner.ts src/infra/outbound/message-action-runner.core-send.test.ts src/infra/outbound/outbound-send-service.ts src/infra/outbound/message.ts src/infra/outbound/message.test.ts src/gateway/protocol/schema/agent.ts src/gateway/server-methods/send.ts src/gateway/server-methods/send.test.ts
  • pnpm protocol:gen:swift
  • pnpm tsgo:core:test
  • git diff --check upstream/main..HEAD
  • pnpm check:changelog-attributions

Linked issue

Fixes #73448

CI red analysis

Latest head was rebased onto current upstream/main and is merge-clean. GitHub has not reported checks yet for the latest pushed head.

Previous remote CI on the prior head was green/skipped across the main CI matrix, workflow sanity, security, Android, build, docs, type, lint, protocol, and node shards. The only prior red check was Run the OpenAI / Opus 4.6 parity gate against the qa-lab mock.

That parity job passed 11/12 mock QA scenarios and failed only thread-memory-isolation with timed out after 45000ms. The artifact summary contained no message, asVoice, audioAsVoice, CLI send, gateway send, or outbound-delivery failure; this PR does not touch thread memory isolation, memory recall, model QA scenario routing, or subagent state. I classify that prior red check as unrelated to this diff. I tried to rerun the failed job, but GitHub rejected it because reruns require repository admin rights.

Local checks on the latest head are green.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • apps/macos/Sources/OpenClawProtocol/GatewayModels.swift (modified, +4/-0)
  • apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift (modified, +4/-0)
  • docs/cli/message.md (modified, +2/-1)
  • src/agents/tools/message-tool.test.ts (modified, +11/-0)
  • src/agents/tools/message-tool.ts (modified, +5/-1)
  • src/cli/program/message/register.send.test.ts (added, +44/-0)
  • src/cli/program/message/register.send.ts (modified, +1/-0)
  • src/gateway/protocol/schema/agent.ts (modified, +1/-0)
  • src/gateway/server-methods/send.test.ts (modified, +31/-0)
  • src/gateway/server-methods/send.ts (modified, +9/-1)
  • src/infra/outbound/message-action-param-keys.ts (modified, +1/-0)
  • src/infra/outbound/message-action-runner.core-send.test.ts (modified, +57/-0)
  • src/infra/outbound/message-action-runner.ts (modified, +2/-0)
  • src/infra/outbound/message.test.ts (modified, +23/-0)
  • src/infra/outbound/message.ts (modified, +3/-0)
  • src/infra/outbound/outbound-send-service.ts (modified, +2/-0)

Code Example

const gifPlayback = readBooleanParam(params, 'gifPlayback') ?? false;
const forceDocument = readBooleanParam(params, 'forceDocument') ?? readBooleanParam(params, 'asDocument') ?? false;
const bestEffort = readBooleanParam(params, 'bestEffort');
const silent = readBooleanParam(params, 'silent');

---

ffmpeg -y -i voice.mp3 -c:a libopus -b:a 64k -application voip -ar 48000 voice.ogg

---

const asVoice = readBooleanParam(params, 'asVoice');
RAW_BUFFERClick to expand / collapse

Bug Report

Summary

The message tool's asVoice parameter is defined in the schema but never read or passed through in handleSendAction, so audio sent with asVoice: true is always delivered as a file attachment instead of a voice message.

Root Cause

In message-action-runner-CkqyWfJZ.js, the handleSendAction function (line ~1024) reads many parameters:

const gifPlayback = readBooleanParam(params, 'gifPlayback') ?? false;
const forceDocument = readBooleanParam(params, 'forceDocument') ?? readBooleanParam(params, 'asDocument') ?? false;
const bestEffort = readBooleanParam(params, 'bestEffort');
const silent = readBooleanParam(params, 'silent');

But there is no readBooleanParam(params, 'asVoice') anywhere in the file. A grep for asVoice\|audioAsVoice\|voice in message-action-runner-CkqyWfJZ.js returns zero matches.

Affected Code

FileLineStatus
openclaw-tools-C7Or31if.js~3251Schema defines asVoice: Type.Optional(Type.Boolean())
message-action-runner-CkqyWfJZ.js~1024 (handleSendAction)Never reads asVoice
feishu/send-BjhQwBW9.js~371 (prepareFeishuVoiceMedia)Correctly handles audioAsVoice: true and transcodes MP3→OGG Opus ✅

Impact

  • Users cannot send native voice messages via the message tool with asVoice: true
  • The Feishu plugin's transcoding pipeline (transcodeToFeishuVoiceOpus) is never triggered from the message tool path
  • TTS auto-voice delivery may also be affected (different code path via auto-TTS)

Workaround

Manually pre-transcode audio to OGG Opus before sending. The Feishu plugin's isFeishuNativeVoiceAudio() correctly recognizes .ogg/.opus files as native voice format:

ffmpeg -y -i voice.mp3 -c:a libopus -b:a 64k -application voip -ar 48000 voice.ogg

Expected Fix

Add to handleSendAction:

const asVoice = readBooleanParam(params, 'asVoice');

And pass it through to executeSendAction so the channel plugin's sendMedia receives audioAsVoice.

extent analysis

TL;DR

The most likely fix is to add a line to read the asVoice parameter in the handleSendAction function and pass it through to executeSendAction.

Guidance

  • Add const asVoice = readBooleanParam(params, 'asVoice'); to handleSendAction to read the asVoice parameter.
  • Pass the asVoice value to executeSendAction so it can be used by the channel plugin's sendMedia function.
  • Verify that the asVoice parameter is being correctly passed through by checking the sendMedia function call in the channel plugin.
  • Test the fix by sending a voice message with asVoice: true and verifying that it is delivered as a native voice message.

Example

const asVoice = readBooleanParam(params, 'asVoice');
// ...
executeSendAction({
  // ...
  audioAsVoice: asVoice,
});

Notes

This fix assumes that the executeSendAction function is correctly handling the audioAsVoice parameter and passing it to the channel plugin's sendMedia function.

Recommendation

Apply the workaround of manually pre-transcoding audio to OGG Opus before sending, or apply the fix by adding the asVoice parameter read and pass-through to handleSendAction. The latter is preferred as it allows for native voice message delivery without manual transcoding.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING