openclaw - ✅(Solved) Fix Bug: message send with local media on openclaw-weixin is misclassified as poll creation [2 pull requests, 1 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#55478Fetched 2026-04-08 01:39:08
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
cross-referenced ×1

When using the message tool with action="send" and a local file path in media for channel="openclaw-weixin", OpenClaw rejects the request before it reaches the channel plugin with:

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

This appears to be a message normalization / validation bug in core, not a Weixin runtime send failure.

Error Message

Observed error: throw new Error('Poll fields require action "poll"; use action "poll" instead of "send".'); I also tried a local hot patch against a compiled bundle to bypass poll detection for send + media/file, restarted the gateway, and still reproduced the error. That suggests there may be more than one compiled/runtime path involved for this validation.

Root Cause

When using the message tool with action="send" and a local file path in media for channel="openclaw-weixin", OpenClaw rejects the request before it reaches the channel plugin with:

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

This appears to be a message normalization / validation bug in core, not a Weixin runtime send failure.

Fix Action

Fix / Workaround

I also tried a local hot patch against a compiled bundle to bypass poll detection for send + media/file, restarted the gateway, and still reproduced the error. That suggests there may be more than one compiled/runtime path involved for this validation.

PR fix notes

PR #65295: fix: avoid misclassifying send poll metadata as poll intent(Weixin plugin)

Description (problem / solution / changelog)

Summary

Describe the problem and fix in 2–5 bullets:

If this PR fixes a plugin beta-release blocker, title it fix(<plugin-id>): beta blocker - <summary> and link the matching Beta blocker: <plugin-name> - <summary> issue labeled beta-blocker. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.

  • Problem: message-action-runner treated any poll-related params on action="send" as poll intent, including metadata-only fields like duration and boolean flags.
  • Why it matters: normal sends could be rejected even when the payload did not actually contain a poll question or options.
  • What changed: send now only treats non-empty pollQuestion or pollOption as real poll intent, and regression tests were added for both the relaxed send path and the retained real-poll rejection path.
  • What did NOT change (scope boundary): poll creation behavior, poll validation rules, and poll-only parameter parsing for actual action="poll" requests were not changed.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #55478
  • Closes #52118
  • Closes #55994
  • This PR fixes a bug or regression

Root Cause (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: the send-side guard reused broad poll-parameter detection that was designed to notice any poll-related fields, not to distinguish real poll payloads from metadata-only params.
  • Missing detection / guardrail: there was no narrower intent check requiring actual poll content such as a question or options before rejecting action="send".
  • Contributing context (if known): tool schemas and channel-specific params can populate poll metadata fields independently of a real poll payload, which made the broad check too aggressive.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/poll-params.test.ts, src/infra/outbound/message-action-runner.core-send.test.ts
  • Scenario the test should lock in: action="send" must succeed when only poll metadata is present, and must still reject when pollQuestion or pollOption indicates a real poll payload.
  • Why this is the smallest reliable guardrail: the regression lives in local parameter classification and send routing, so focused unit tests cover the failure mode without requiring channel end-to-end setup.
  • Existing test that already covers this (if any): there was partial coverage for zero-valued poll params, but not for non-question metadata-only poll params on send.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

message send no longer rejects non-poll sends just because poll metadata fields are present. Real poll payloads on action="send" are still rejected and must use action="poll".

Diagram (if applicable)

For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write N/A.

Before:
[send action + poll metadata only] -> [broad poll param check] -> [rejected as poll]

After:
[send action + poll metadata only] -> [real poll intent check] -> [sent normally]
[send action + pollQuestion/pollOption] -> [real poll intent check] -> [rejected, use poll action]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: local repo checkout
  • Model/provider: N/A
  • Integration/channel (if any): outbound message action routing
  • Relevant config (redacted): minimal test config with outbound test plugin

Steps

  1. Run runMessageAction with action: "send" and metadata-only poll fields such as pollDurationHours, pollMulti, or pollAnonymous.
  2. Observe behavior before the fix: the request is rejected with Poll fields require action "poll".
  3. Run the same scenario after the fix and confirm the send succeeds.
  4. Run runMessageAction with action: "send" plus pollQuestion or pollOption.
  5. Confirm the request is still rejected and must use action: "poll".

Expected

  • Metadata-only poll params do not force action="send" into the poll-only rejection path.
  • Real poll payloads still require action="poll".

Actual

  • Before the fix, metadata-only poll params could incorrectly trigger poll rejection.
  • After the fix, only real poll content triggers that rejection.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: targeted unit tests for hasRealPollIntent and runMessageAction send routing were run locally.
  • Edge cases checked: empty question, empty option array, metadata-only params, and real poll question payloads.
  • What you did not verify: live channel delivery against real Telegram/Discord/other integrations was not manually exercised.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: some caller may have implicitly depended on metadata-only poll params being rejected on action="send".
    • Mitigation: the new tests lock the intended behavior to actual poll content only, which matches the error message and action contract more closely.

Changed files

  • src/infra/outbound/message-action-runner.core-send.test.ts (modified, +91/-0)
  • src/infra/outbound/message-action-runner.send-validation.test.ts (modified, +26/-15)
  • src/infra/outbound/message-action-runner.ts (modified, +2/-2)
  • src/poll-params.test.ts (modified, +19/-1)
  • src/poll-params.ts (modified, +23/-0)

PR #65301: Fix/send poll intent detection clean

Description (problem / solution / changelog)

Summary

Describe the problem and fix in 2–5 bullets:

If this PR fixes a plugin beta-release blocker, title it fix(<plugin-id>): beta blocker - <summary> and link the matching Beta blocker: <plugin-name> - <summary> issue labeled beta-blocker. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.

  • Problem: message-action-runner treated any poll-related params on action="send" as poll intent, including metadata-only fields like duration and boolean flags.
  • Why it matters: normal sends could be rejected even when the payload did not actually contain a poll question or options.
  • What changed: send now only treats non-empty pollQuestion or pollOption as real poll intent, and regression tests were added for both the relaxed send path and the retained real-poll rejection path.
  • What did NOT change (scope boundary): poll creation behavior, poll validation rules, and poll-only parameter parsing for actual action="poll" requests were not changed.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #55478
  • Closes #52118
  • Closes #55994
  • This PR fixes a bug or regression

Root Cause (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: the send-side guard reused broad poll-parameter detection that was designed to notice any poll-related fields, not to distinguish real poll payloads from metadata-only params.
  • Missing detection / guardrail: there was no narrower intent check requiring actual poll content such as a question or options before rejecting action="send".
  • Contributing context (if known): tool schemas and channel-specific params can populate poll metadata fields independently of a real poll payload, which made the broad check too aggressive.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/poll-params.test.ts, src/infra/outbound/message-action-runner.core-send.test.ts
  • Scenario the test should lock in: action="send" must succeed when only poll metadata is present, and must still reject when pollQuestion or pollOption indicates a real poll payload.
  • Why this is the smallest reliable guardrail: the regression lives in local parameter classification and send routing, so focused unit tests cover the failure mode without requiring channel end-to-end setup.
  • Existing test that already covers this (if any): there was partial coverage for zero-valued poll params, but not for non-question metadata-only poll params on send.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

message send no longer rejects non-poll sends just because poll metadata fields are present. Real poll payloads on action="send" are still rejected and must use action="poll".

Diagram (if applicable)

For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write N/A.

Before:
[send action + poll metadata only] -> [broad poll param check] -> [rejected as poll]

After:
[send action + poll metadata only] -> [real poll intent check] -> [sent normally]
[send action + pollQuestion/pollOption] -> [real poll intent check] -> [rejected, use poll action]

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: local repo checkout
  • Model/provider: N/A
  • Integration/channel (if any): outbound message action routing
  • Relevant config (redacted): minimal test config with outbound test plugin

Steps

  1. Run runMessageAction with action: "send" and metadata-only poll fields such as pollDurationHours, pollMulti, or pollAnonymous.
  2. Observe behavior before the fix: the request is rejected with Poll fields require action "poll".
  3. Run the same scenario after the fix and confirm the send succeeds.
  4. Run runMessageAction with action: "send" plus pollQuestion or pollOption.
  5. Confirm the request is still rejected and must use action: "poll".

Expected

  • Metadata-only poll params do not force action="send" into the poll-only rejection path.
  • Real poll payloads still require action="poll".

Actual

  • Before the fix, metadata-only poll params could incorrectly trigger poll rejection.
  • After the fix, only real poll content triggers that rejection.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: targeted unit tests for hasRealPollIntent and runMessageAction send routing were run locally.
  • Edge cases checked: empty question, empty option array, metadata-only params, and real poll question payloads.
  • What you did not verify: live channel delivery against real Telegram/Discord/other integrations was not manually exercised.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: some caller may have implicitly depended on metadata-only poll params being rejected on action="send".
    • Mitigation: the new tests lock the intended behavior to actual poll content only, which matches the error message and action contract more closely.

Changed files

  • src/infra/outbound/message-action-runner.core-send.test.ts (modified, +91/-0)
  • src/infra/outbound/message-action-runner.send-validation.test.ts (modified, +26/-15)
  • src/infra/outbound/message-action-runner.ts (modified, +2/-2)
  • src/poll-params.test.ts (modified, +19/-1)
  • src/poll-params.ts (modified, +23/-0)

Code Example

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

---

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

---

[tools] message failed: Poll fields require action "poll"; use action "poll" instead of "send".

---

if (action === "send" && hasPollCreationParams(params))
  throw new Error('Poll fields require action "poll"; use action "poll" instead of "send".');
RAW_BUFFERClick to expand / collapse

Summary

When using the message tool with action="send" and a local file path in media for channel="openclaw-weixin", OpenClaw rejects the request before it reaches the channel plugin with:

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

This appears to be a message normalization / validation bug in core, not a Weixin runtime send failure.

Environment

  • OpenClaw: 2026.3.13
  • Channel: openclaw-weixin
  • Account: 4b4f3c849bb9-im-bot
  • Runtime: macOS / Node 22.22.0

Repro

Using the message tool (or equivalent internal message send path), send a local file:

  • action: send
  • channel: openclaw-weixin
  • media: /Users/js/Desktop/docs/openclaw-guides/weixin-multi-agent-guide-2026-03-27.pdf
  • empty message

Observed error:

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

I reproduced the same behavior with:

  • .md
  • .pdf

So this is not file-type specific.

Evidence this happens before plugin send

Gateway log shows:

[tools] message failed: Poll fields require action "poll"; use action "poll" instead of "send".

The Weixin channel itself is healthy/running. The request is blocked before outbound plugin handling.

Suspected cause

Core message validation contains:

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

The tool bridge appears to be passing poll-related default fields on ordinary sends (e.g. empty / false / zero poll fields), and one of them is being treated as present.

This causes regular file sends to be misclassified as poll creation.

Expected behavior

action="send" with media set to a local file path should proceed as a normal outbound media/file send for openclaw-weixin.

At minimum, empty/default poll fields should not cause send requests to be rejected.

Suggested fix

One of:

  1. In core, only treat poll params as present when explicitly provided with meaningful values.
  2. Do not include poll default fields in tool/message bridge payloads for ordinary send actions.
  3. Add a regression test for message send --media <local-file> on channels that support send-via-send+media.

Notes

I also tried a local hot patch against a compiled bundle to bypass poll detection for send + media/file, restarted the gateway, and still reproduced the error. That suggests there may be more than one compiled/runtime path involved for this validation.

extent analysis

Fix Plan

To resolve the issue, we will implement the suggested fix by modifying the core message validation to only treat poll params as present when explicitly provided with meaningful values.

Step-by-Step Solution

  1. Modify the core message validation: Update the condition to check for poll creation params only when they have meaningful values.

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

   Define the `hasMeaningfulPollCreationParams` function to check for non-empty or non-zero poll fields:
   ```javascript
function hasMeaningfulPollCreationParams(params) {
  // Check for non-empty or non-zero poll fields
  return Object.keys(params).some(key => {
    if (key.startsWith('poll')) {
      return params[key] !== null && params[key] !== undefined && params[key] !== '';
    }
    return false;
  });
}
  1. Update the tool bridge: Ensure that the tool bridge does not include poll default fields in payloads for ordinary send actions.

if (action === "send") { // Remove poll default fields from the payload delete payload.poll; delete payload.pollOptions; }

3. **Add a regression test**:
   Create a test case for `message send --media <local-file>` on channels that support send-via-send+media to prevent similar issues in the future.

### Verification
To verify that the fix worked, send a local file using the `message` tool with `action="send"` and `channel="openclaw-weixin"`:
```bash
message send --action send --channel openclaw-weixin --media /path/to/local/file.pdf

The request should proceed as a normal outbound media/file send without being rejected due to poll fields.

Extra Tips

  • Ensure that the hasMeaningfulPollCreationParams function is correctly implemented to handle different types of poll fields.
  • Review the tool bridge implementation to prevent similar issues with other action types.
  • Consider adding additional logging or debugging statements to help diagnose similar issues in the future.

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…

FAQ

Expected behavior

action="send" with media set to a local file path should proceed as a normal outbound media/file send for openclaw-weixin.

At minimum, empty/default poll fields should not cause send requests to be rejected.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING