openclaw - ✅(Solved) Fix [Bug] Feishu send action: card field incorrectly marked as required in schema (3.23 regression) [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#53697Fetched 2026-04-08 01:24:41
View on GitHub
Comments
1
Participants
2
Timeline
14
Reactions
0
Timeline (top)
referenced ×10closed ×1commented ×1cross-referenced ×1

Error Message

When trying to send an image via the Feishu channel using the `message` tool with `media` parameter (file path or URL), the tool fails with validation error: if (card && mediaUrl) throw new Error(`Feishu ${ctx.action} does not support card with media.`);

  • Omit card → schema validation error

Root Cause

File: dist/channel-1opGQxxp.js, line ~603-608

The implementation logic is correct:

const card = ctx.params.card && typeof ctx.params.card === \"object\" ? ctx.params.card : void 0;
// ...later:
if (card && mediaUrl) throw new Error(\`Feishu \${ctx.action} does not support card with media.\`);
else if (mediaUrl) result = await sendMedia({ ... });

However, the schema layer incorrectly marks card as required, creating a catch-22:

  • Omit card → schema validation error
  • Include card: {} → enters if (card) branch → sends as card → fails

Fix Action

Workaround

None available — the schema validation blocks the action entirely.

PR fix notes

PR #53715: fix: mark card field as optional in message tool schema

Description (problem / solution / changelog)

Fixes #53697. The createMessageToolCardSchema() helper returned a bare Type.Object() which TypeBox marks as required when merged into the parent message tool schema. This blocked media-only sends on Feishu (and MSTeams) with a "must have required property card" validation error. Wrap the return value in Type.Optional() so the card field is correctly excluded from JSON Schema required.

Summary

  • Problem: When sending images through Feishu using { action: "send", media: "/path/to/image.png" }, schema validation rejects the payload with "card: must have required property card". Including an empty card: {} bypasses validation but then hits the runtime guard "Feishu send does not support card with media." — a catch-22.
  • Why it matters: Users cannot send media-only messages through Feishu or MSTeams channels at all. The card field is only needed for interactive card messages, not for text or media sends.
  • What changed: Wrapped createMessageToolCardSchema() return value in Type.Optional() so TypeBox excludes card from the JSON Schema required array. Added 2 tests: schema optionality assertion + media-only send integration test.
  • What did NOT change (scope boundary): The card schema shape itself is unchanged (Type.Object({}, { additionalProperties: true })). The runtime handling logic in channel.ts is unchanged. No new config, no fallback behavior.

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 #53697
  • Related # N/A
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: createMessageToolCardSchema() in src/plugin-sdk/channel-actions.ts returns Type.Object(...) without Type.Optional(). When channel plugins (Feishu, MSTeams) contribute this as { properties: { card: createMessageToolCardSchema() } }, the schema merge in message-action-discovery.ts passes it into Type.Object({ ...props }), which makes card a required field in the final JSON Schema.
  • Missing detection / guardrail: No test verified that the card field was optional in the contributed schema. Existing tests only tested card sends (which include the field) and text sends (which happen to not trigger the validation path in unit tests because they bypass schema validation).
  • Prior context: The createMessageToolCardSchema() helper was added alongside card support for Feishu and MSTeams. The Type.Optional() wrapper was likely omitted by oversight, since the implementation code in channel.ts already treats card as optional (lines 508-511).
  • Why this regressed now: Likely present since card schema was first introduced; may have been masked if schema validation was previously lenient or if a TypeBox version change tightened required-field enforcement.
  • If unknown, what was ruled out: N/A — root cause is clear from TypeBox semantics.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/feishu/src/channel.test.ts
  • Scenario the test should lock in: (1) The card property in the Feishu tool schema must carry TypeBox's Optional marker. (2) A media-only send (no card, no text) succeeds through the action handler.
  • Why this is the smallest reliable guardrail: The schema optionality test directly asserts the TypeBox Optional symbol on the contributed card property — if someone removes Type.Optional(), the test fails immediately.
  • Existing test that already covers this (if any): None — existing "sends card messages" test only covers the card-present path.
  • If no new test is added, why not: N/A — 2 new tests added.

User-visible / Behavior Changes

  • Feishu and MSTeams media-only sends (without a card) now pass schema validation and execute correctly.
  • No change for card sends or text-only sends.

Security Impact (required)

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

Repro + Verification

Environment

  • OS: macOS (also reproducible on Linux)
  • Runtime/container: Node.js 23.x
  • Model/provider: N/A
  • Integration/channel (if any): Feishu
  • Relevant config (redacted): Feishu channel configured with valid credentials

Steps

  1. Configure a Feishu channel
  2. Send a message with only media: { action: "send", channel: "feishu", to: "chat:oc_group_1", media: "/path/to/image.png" }
  3. Observe validation error

Expected

  • Media send succeeds (no schema validation error for missing card)

Actual

  • Before this PR: Validation failed for tool "message": card: must have required property "card"
  • After this PR: media send proceeds to the runtime handler

Evidence

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

2 new tests in extensions/feishu/src/channel.test.ts: schema optionality assertion and media-only send test. All existing Feishu channel tests remain green.

Human Verification (required)

  • Verified scenarios: TypeBox schema output confirms card is excluded from required array after the fix; Type.Optional symbol is present on the card schema. Media-only send handler test passes.
  • Edge cases checked: Card-present sends still work (existing test). Text-only sends still work (existing test). Empty env (no card, no text, no media) still throws correct error.
  • What you did not verify: Full pnpm build and pnpm check (pnpm not available locally — relies on CI). Manual end-to-end Feishu media send with real API.

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 — the card field was always intended to be optional; this restores correct behavior.
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert this single commit.
  • Files/config to restore: src/plugin-sdk/channel-actions.ts to prior version; remove new tests from extensions/feishu/src/channel.test.ts.
  • Known bad symptoms reviewers should watch for: Any schema validation change for channels that use createMessageToolCardSchema() (currently Feishu and MSTeams). If a channel plugin relied on card being required (unlikely — none do), that would be a behavior change.

Risks and Mitigations

  • Risk: MSTeams also uses createMessageToolCardSchema() — the fix applies there too.
    • Mitigation: This is intentional and correct. MSTeams card field should also be optional for the same reason. The MSTeams action handler already treats card as optional in its implementation.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/feishu/src/channel.test.ts (modified, +39/-0)
  • src/agents/tools/message-tool.test.ts (modified, +36/-1)
  • src/plugin-sdk/channel-actions.ts (modified, +8/-6)

Code Example

Validation failed for tool \"message\":
  - card: must have required property \"card\"

---

{
  \"action\": \"send\",
  \"channel\": \"feishu\",
  \"media\": \"/path/to/image.png\"
}

---

Feishu send does not support card with media.

---

const card = ctx.params.card && typeof ctx.params.card === \"object\" ? ctx.params.card : void 0;
// ...later:
if (card && mediaUrl) throw new Error(\`Feishu \${ctx.action} does not support card with media.\`);
else if (mediaUrl) result = await sendMedia({ ... });
RAW_BUFFERClick to expand / collapse

Bug Description

OpenClaw version: 2026.3.23-2
Channel: Feishu
Action: message send (with media)

When trying to send an image via the Feishu channel using the `message` tool with `media` parameter (file path or URL), the tool fails with validation error:

Validation failed for tool \"message\":
  - card: must have required property \"card\"

Steps to Reproduce

  1. Have a configured Feishu channel
  2. Send a message with media:
{
  \"action\": \"send\",
  \"channel\": \"feishu\",
  \"media\": \"/path/to/image.png\"
}

Result: Schema validation fails: card: must have required property card

If you bypass the schema by adding `card: {}` (empty object), the tool call proceeds but then fails with:

Feishu send does not support card with media.

Root Cause

File: dist/channel-1opGQxxp.js, line ~603-608

The implementation logic is correct:

const card = ctx.params.card && typeof ctx.params.card === \"object\" ? ctx.params.card : void 0;
// ...later:
if (card && mediaUrl) throw new Error(\`Feishu \${ctx.action} does not support card with media.\`);
else if (mediaUrl) result = await sendMedia({ ... });

However, the schema layer incorrectly marks card as required, creating a catch-22:

  • Omit card → schema validation error
  • Include card: {} → enters if (card) branch → sends as card → fails

Expected Behavior

The card field should be optional, allowing:

  1. Text-only messages (no card, no media)
  2. Media messages (no card, media URL/path)
  3. Card messages (card object provided, no media)

Workaround

None available — the schema validation blocks the action entirely.

extent analysis

Fix Plan

To fix the issue, we need to update the schema to make the card field optional. Here are the steps:

  • Update the schema definition for the message tool to make card optional:
const schema = {
  type: 'object',
  properties: {
    action: { type: 'string' },
    channel: { type: 'string' },
    media: { type: 'string', nullable: true },
    card: { type: 'object', nullable: true } // Make card optional
  },
  required: ['action', 'channel']
};
  • Remove the required constraint for the card property.

Example Code

Here's an example of how the updated schema can be used:

const messageTool = {
  schema: {
    type: 'object',
    properties: {
      action: { type: 'string' },
      channel: { type: 'string' },
      media: { type: 'string', nullable: true },
      card: { type: 'object', nullable: true }
    },
    required: ['action', 'channel']
  },
  async execute(ctx) {
    const { action, channel, media, card } = ctx.params;
    // ... implementation logic ...
  }
};

Verification

To verify that the fix worked, try sending a message with media using the message tool:

{
  "action": "send",
  "channel": "feishu",
  "media": "/path/to/image.png"
}

The message should be sent successfully without any schema validation errors.

Extra Tips

  • Make sure to update the schema definition in the correct location, which is likely in the dist/channel-1opGQxxp.js file.
  • After updating the schema, restart the application to ensure the changes take effect.
  • Test the message tool with different scenarios, such as text-only messages, media messages, and card messages, to ensure that the fix works as expected.

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