mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
fix(feishu): skip empty-text messages with no media to prevent blank session turns (#74634) (#74661)
Feishu delivers empty-text events (e.g. {"text":""}) when users send
blank messages or when a media-only message produces no text content.
Writing a blank user turn to the session file causes downstream LLM
providers such as MiniMax to reject requests with:
invalid params, messages must not be empty (2013)
Guard at the point after media resolution: if ctx.content.trim() is
empty AND mediaList is empty, log the skip and return without queuing
a reply. This preserves all existing behaviour for text, media, and
mixed messages.
Regression test: dispatch a DM with {"text":""} (no media), assert
mockDispatchReplyFromConfig is not called.
Closes #74634. Thanks @xdengli.
This commit is contained in:
@@ -48,6 +48,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/output: drop copied inbound metadata-only assistant replay turns before provider replay instead of synthesizing a placeholder, so Telegram and other channels cannot receive `[assistant copied inbound metadata omitted]` as model output. Fixes #74745. Thanks @adamwdear and @Marvae.
|
||||
- Doctor/memory: suppress skipped embedding-readiness warnings for key-optional providers such as Ollama and LM Studio while preserving timeout and not-ready diagnostics. Fixes #74608 and #73882. Thanks @hclsys.
|
||||
- Channels/groups: preserve observe-only turn suppression for prepared dispatch paths and restore deprecated channel turn runtime aliases, so passive observer/group flows stay silent while older plugins keep compiling. Thanks @vincentkoc.
|
||||
- Feishu: skip empty-text messages (e.g. `{"text":""}`) that carry no media, so no blank user turn is written to the session and downstream LLM providers cannot reject the request with "messages must not be empty". (#74634) Thanks @xdengli and @hclsys.
|
||||
- Feishu/Bitable: clean up newly created placeholder rows whose fields contain only default empty values while preserving meaningful link, attachment, user, number, boolean, and location values during create-app cleanup. (#73920) Carries forward #40602. Thanks @boat2moon.
|
||||
- macOS app: keep attach-only mode and the Debug Settings launchd toggle marker-only, so launching with `--attach-only`/`--no-launchd` no longer uninstalls the Gateway LaunchAgent or drops active sessions. (#72174) Thanks @DolencLuka.
|
||||
- Plugin SDK: restore the deprecated `plugin-sdk/zalouser` command-auth facade so published Lark/Zalo plugins that import it load on current hosts. Fixes #74702. Thanks @Goron01.
|
||||
|
||||
@@ -2999,4 +2999,42 @@ describe("handleFeishuMessage command authorization", () => {
|
||||
await Promise.all([dispatchMessage({ cfg, event }), dispatchMessage({ cfg, event })]);
|
||||
expect(mockDispatchReplyFromConfig).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("skips empty-text messages with no media to prevent blank user turns in session (#74634)", async () => {
|
||||
// Feishu can deliver { "text": "" } events (empty-text or media-stripped
|
||||
// messages). Writing blank user content to the session causes downstream
|
||||
// LLM providers such as MiniMax to reject requests with "messages must not
|
||||
// be empty". The handler should drop such events before queuing a reply.
|
||||
mockShouldComputeCommandAuthorized.mockReturnValue(false);
|
||||
|
||||
const cfg: ClawdbotConfig = {
|
||||
channels: {
|
||||
feishu: {
|
||||
dmPolicy: "open",
|
||||
allowFrom: ["*"],
|
||||
},
|
||||
},
|
||||
} as ClawdbotConfig;
|
||||
|
||||
const event: FeishuMessageEvent = {
|
||||
sender: {
|
||||
sender_id: {
|
||||
open_id: "ou-empty-text-sender",
|
||||
},
|
||||
},
|
||||
message: {
|
||||
message_id: "msg-empty-text-74634",
|
||||
chat_id: "oc-dm",
|
||||
chat_type: "p2p",
|
||||
message_type: "text",
|
||||
// Feishu encodes empty text as {"text":""}
|
||||
content: JSON.stringify({ text: "" }),
|
||||
},
|
||||
};
|
||||
|
||||
await dispatchMessage({ cfg, event });
|
||||
|
||||
// No reply should be dispatched: empty message is silently skipped
|
||||
expect(mockDispatchReplyFromConfig).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -859,6 +859,19 @@ export async function handleFeishuMessage(params: {
|
||||
log,
|
||||
accountId: account.accountId,
|
||||
});
|
||||
// Skip messages with no text content and no media attachments. Feishu can
|
||||
// deliver empty-text events (e.g. `{"text":""}`) when a user sends a blank
|
||||
// message or when media parsing produces an empty string. Writing a blank
|
||||
// user turn to the session causes downstream LLM providers (e.g. MiniMax)
|
||||
// to reject the request with "messages must not be empty" errors. Logging
|
||||
// the skip avoids silent loss without polluting the agent session.
|
||||
if (!ctx.content.trim() && mediaList.length === 0) {
|
||||
log(
|
||||
`feishu[${account.accountId}]: skipping empty message (no text, no media) from ${ctx.senderOpenId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const mediaPayload = buildAgentMediaPayload(mediaList);
|
||||
const audioTranscript = await resolveFeishuAudioPreflightTranscript({
|
||||
cfg: effectiveCfg,
|
||||
|
||||
Reference in New Issue
Block a user