Skip to content

message tool: Feishu send action polluted by poll schema/guard prevents file send #42820

Description

@charzhou

Summary

message tool cannot send a plain file to a Feishu chat using action: "send" when the tool schema includes poll-related fields. Any send call that happens to carry poll params (which the model tends to auto-fill from the schema) is rejected with:

Poll fields require action "poll"; use action "poll" instead of "send".

This makes ordinary "send + attachment" semantics unusable for Feishu when the general message tool schema exposes poll creation parameters.

Environment

  • Runtime: OpenClaw (macOS, /opt/homebrew install)
  • Path in use: /opt/homebrew/lib/node_modules/openclaw/dist
  • Channel: Feishu
  • Tool: message (agent tools.message)
  • Action: send
  • Payload: plain text message + local txt file attachment

Expected behavior

  • action: "send" should allow sending a normal message (optionally with an attachment) on Feishu, even if the generic message tool supports polls on other channels.
  • The presence of poll-related fields in the tool schema should not break send semantics for channels that don't need polls, especially when the user is just trying to send a file.

Actual behavior

When using the message tool with action: "send" on Feishu to send a local txt file, the call fails with:

Poll fields require action "poll"; use action "poll" instead of "send".

From the calling side:

  • Semantic intent is: action = "send", channel = Feishu, message + txt attachment.
  • The tool schema, however, includes a full set of poll fields (pollQuestion, pollOption, pollDurationHours, pollMulti, pollDurationSeconds, pollAnonymous, pollPublic, etc.).
  • The model tends to populate these poll fields based on the schema, even when the prompt says "don't send a poll".
  • As a result, the generic guard sees hasPollCreationParams(params) == true on a send action and throws the error above, before the request ever reaches Feishu.

Relevant code paths (reverse-engineered from dist)

From the compiled bundle in /opt/homebrew/lib/node_modules/openclaw/dist (filenames shortened for clarity):

  1. Message tool schema construction
function buildMessageToolSchemaProps(options) {
  return {
    ...buildRoutingSchema(),
    ...buildSendSchema(options),
    ...buildReactionSchema(),
    ...buildFetchSchema(),
    ...buildPollSchema({ includeTelegramExtras: options.includeTelegramPollExtras }),
    ...buildChannelTargetSchema(),
    ...buildStickerSchema(),
    ...buildThreadSchema(),
    ...buildEventSchema(),
    ...buildModerationSchema(),
    ...buildGatewaySchema(),
    ...buildChannelManagementSchema(),
    ...buildPresenceSchema(),
  };
}

buildPollSchema expands both poll creation fields and poll voting fields into the same argument object that action: "send" uses.

  1. Poll param detection guard
function hasPollCreationParams(params) {
  for (const key of POLL_CREATION_PARAM_NAMES) {
    const def = POLL_CREATION_PARAM_DEFS[key];
    const value = readPollParamRaw(params, key);
    // string / stringArray / number / boolean checks ...
    // any non-empty / true / finite value → true
  }
  return false;
}

// later in the dispatch path

if (action === "send" && hasPollCreationParams(params)) {
  throw new Error(
    'Poll fields require action "poll"; use action "poll" instead of "send".'
  );
}

Behaviorally, this means:

  • If any poll creation field has a "truthy" value (non-empty string, finite number, true, etc.), hasPollCreationParams returns true.
  • Combined with action === "send", the guard throws the error above.
  • Since the tool schema exposes these poll fields unconditionally, the model tends to fill them even when the user only wants to send a file, making the error effectively unavoidable in real agent flows.

Why this is a problem in practice

In an agent-driven environment:

  • Models generally try to fill all visible/encouraged fields from a tool schema, especially optional ones that look relevant.
  • Asking the model via prompt "don't use poll fields" is a soft constraint and not reliable.
  • As long as the tool schema exposes poll creation parameters on the same message tool used for plain send, agent calls to Feishu send + attachment will randomly trip the guard, even though Feishu itself doesn't need (or support) those poll parameters for simple file sends.

In this specific case, that made it impossible to:

  • Use the message tool to send a simple txt file to a Feishu chat with action: "send".

Local production hotfix (for context only)

On this machine, to unblock Feishu file sends immediately, I applied a dist-level hotfix under:

  • /opt/homebrew/lib/node_modules/openclaw/dist

Important: this is not a proposed upstream fix, just documenting what was done locally so you understand the symptom and the minimal remediation that worked in practice.

Changes (applied across several dist bundles: reply-*, plugin-sdk/reply-*, plugin-sdk/dispatch-*, pi-embedded-*, compact-*):

  1. Remove poll schema from message tool props:
-  ...buildFetchSchema(),
-  ...buildPollSchema({ includeTelegramExtras: options.includeTelegramPollExtras }),
-  ...buildChannelTargetSchema(),
+  ...buildFetchSchema(),
+  ...buildChannelTargetSchema(),
  1. Remove the send+poll guard:
-if (action === "send" && hasPollCreationParams(params)) {
-  throw new Error(
-    'Poll fields require action "poll"; use action "poll" instead of "send".'
-  );
-}

After this change and a openclaw gateway restart, a Feishu message tool call with:

  • action: "send"
  • channel: Feishu
  • message + local txt file

started working as expected (file delivered, no poll error).

Suggested upstream fix

Instead of relying on dist hotfixes, a more robust upstream fix could be one of:

  1. Separate schemas for send vs poll

    • Don't expose poll creation fields on the same schema used for generic send.
    • Keep poll as a distinct action with its own (narrow) schema, so models can't accidentally apply poll fields when the user only wants send + attachment.
  2. Action-aware sanitization

    • Keep the unified schema if necessary, but at runtime:
      • For action: "send", automatically strip all poll creation fields from params before the guard / channel handler.
      • Optionally, keep a soft log/diagnostic if poll params were present (to help detect model misuse), but don't throw.
    • Reserve the strict "Poll fields require action \"poll\"" error for cases where the caller actually uses action: "poll" with malformed poll input.
  3. Channel-aware poll support

    • For channels where polls are not supported (or not implemented yet), don't include poll fields in the message tool schema at all.
    • That would also reduce schema size and make agent behavior more predictable per-channel.

Request

Please adjust the message tool design so that:

  • action: "send" for Feishu (and other channels) is not blocked just because poll creation fields exist in the shared schema.
  • Either by splitting schemas, sanitizing poll fields when action=send, or by scoping poll fields only to channels/actions that explicitly support them.

Happy to share local bundle snippets or a minimal reproducer yaml if that helps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions