openclaw - ✅(Solved) Fix Cross-provider message send leaks source session accountId to target channel [1 pull requests]

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…

Root Cause

In message-action-runner, the accountId resolution at the send path:

let accountId = readStringParam(params, "accountId") ?? input.defaultAccountId;

input.defaultAccountId comes from the source session's delivery context. For a Telegram session, this is "default". When crossContext.allowAcrossProviders is enabled and the target channel is a different provider (e.g., openclaw-weixin), this "default" accountId is passed through to the target channel's outbound handler.

The target channel plugin then tries to resolve an account named "default", which doesn't exist, so the send fails with "not configured".

Fix Action

Workaround

Channel plugins can defensively validate the received accountId against their own registered accounts and fall back to their own resolution logic. Example fix in the weixin plugin:

// Before (broken for cross-provider):
const accountId = ctx.accountId || resolveOutboundAccountId(ctx.cfg, ctx.to);

// After (defensive validation):
const accountId = resolveValidAccountId(ctx.cfg, ctx.accountId)
  || resolveOutboundAccountId(ctx.cfg, ctx.to);

Where resolveValidAccountId checks if the given accountId exists in the channel's registered accounts and returns undefined if not.

PR fix notes

PR #69529: fix: prevent defaultAccountId leak in cross-provider message sends

Description (problem / solution / changelog)

Summary

Two bug fixes bundled in this PR:


Fix 1: #69525 — Cross-provider message send leaks source session accountId to target channel

When sending messages across providers (e.g. Telegram → Feishu) with allowAcrossProviders: true, the source session's defaultAccountId was incorrectly propagated to the target channel plugin, which has no account registered under that name.

File: src/infra/outbound/message-action-runner.ts

const explicitlyProvidedAccountId = readStringParam(params, "accountId");
const allowAcrossProviders = cfg.tools?.message?.crossContext?.allowAcrossProviders === true;
const isCrossProvider = Boolean(
  allowAcrossProviders &&
    input.toolContext?.currentChannelProvider &&
    input.toolContext.currentChannelProvider !== channel,
);
let accountId = explicitlyProvidedAccountId ?? (isCrossProvider ? undefined : input.defaultAccountId);

Fix 2: #69482 — Telegram "Allow always" source field rejected by gateway

Telegram "Allow always" approval handler writes source: "allow-always", but the gateway schema for exec.approvals.set didn't include this field, causing openclaw approvals set --gateway to reject entries with unexpected property 'source'.

File: src/gateway/protocol/schema/exec-approvals.ts

Added source: Type.Optional(Type.String()) to ExecApprovalsAllowlistEntrySchema.


Also included: #69475 (partial) — Skill description fixes

Updated SKILL.md description fields for gog and session-logs to include Use when: trigger conditions per the Anthropic skill spec.

Files: skills/gog/SKILL.md, skills/session-logs/SKILL.md

Changed files

  • skills/gog/SKILL.md (modified, +1/-1)
  • skills/session-logs/SKILL.md (modified, +1/-1)
  • src/gateway/protocol/schema/exec-approvals.ts (modified, +3/-0)
  • src/infra/outbound/message-action-runner.test.ts (modified, +66/-0)
  • src/infra/outbound/message-action-runner.ts (modified, +13/-1)

Code Example

let accountId = readStringParam(params, "accountId") ?? input.defaultAccountId;

---

message(action="send", channel="openclaw-weixin", target="<weixin-user-id>", message="test")

---

// Before (broken for cross-provider):
const accountId = ctx.accountId || resolveOutboundAccountId(ctx.cfg, ctx.to);

// After (defensive validation):
const accountId = resolveValidAccountId(ctx.cfg, ctx.accountId)
  || resolveOutboundAccountId(ctx.cfg, ctx.to);
RAW_BUFFERClick to expand / collapse

Bug Description

When using the message tool to send a cross-provider message (e.g., from a Telegram session to a WeChat/weixin channel), the source session's accountId (typically "default") is passed to the target channel plugin's sendText/sendMedia handler.

Root Cause

In message-action-runner, the accountId resolution at the send path:

let accountId = readStringParam(params, "accountId") ?? input.defaultAccountId;

input.defaultAccountId comes from the source session's delivery context. For a Telegram session, this is "default". When crossContext.allowAcrossProviders is enabled and the target channel is a different provider (e.g., openclaw-weixin), this "default" accountId is passed through to the target channel's outbound handler.

The target channel plugin then tries to resolve an account named "default", which doesn't exist, so the send fails with "not configured".

Expected Behavior

When performing a cross-provider send, the core should either:

  1. Clear/reset the accountId when the target provider differs from the source provider, allowing the target channel to resolve its own account
  2. Or explicitly pass undefined so the target channel's fallback logic (e.g., resolveOutboundAccountId) can kick in

Steps to Reproduce

  1. Configure two channels from different providers (e.g., Telegram + WeChat/weixin)
  2. Set tools.message.crossContext.allowAcrossProviders: true
  3. From a Telegram session, use the message tool to send to a weixin target:
    message(action="send", channel="openclaw-weixin", target="<weixin-user-id>", message="test")
  4. The send fails because accountId="default" is passed to the weixin plugin, which cannot find an account with that id

Workaround

Channel plugins can defensively validate the received accountId against their own registered accounts and fall back to their own resolution logic. Example fix in the weixin plugin:

// Before (broken for cross-provider):
const accountId = ctx.accountId || resolveOutboundAccountId(ctx.cfg, ctx.to);

// After (defensive validation):
const accountId = resolveValidAccountId(ctx.cfg, ctx.accountId)
  || resolveOutboundAccountId(ctx.cfg, ctx.to);

Where resolveValidAccountId checks if the given accountId exists in the channel's registered accounts and returns undefined if not.

Environment

  • OpenClaw version: 2026.4.12
  • Channel plugin: @tencent-weixin/openclaw-weixin v2.1.8

extent analysis

TL;DR

To fix the cross-provider message sending issue, update the target channel plugin to defensively validate the received accountId and fall back to its own resolution logic if the account is not found.

Guidance

  • Update the message-action-runner to clear or reset the accountId when the target provider differs from the source provider.
  • Modify the target channel plugin to validate the received accountId against its own registered accounts using a function like resolveValidAccountId.
  • If the accountId is invalid, use the plugin's fallback logic, such as resolveOutboundAccountId, to determine the correct account.
  • Test the updated plugin with cross-provider sends to ensure the issue is resolved.

Example

const accountId = resolveValidAccountId(ctx.cfg, ctx.accountId)
  || resolveOutboundAccountId(ctx.cfg, ctx.to);

This code snippet demonstrates how to defensively validate the accountId and fall back to the plugin's resolution logic if necessary.

Notes

This fix assumes that the target channel plugin has a resolveValidAccountId function to check if the given accountId exists in the channel's registered accounts. If this function does not exist, it will need to be implemented.

Recommendation

Apply the workaround by updating the target channel plugin to defensively validate the received accountId and fall back to its own resolution logic. This approach allows for a more flexible and robust solution that can handle different provider scenarios.

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

openclaw - ✅(Solved) Fix Cross-provider message send leaks source session accountId to target channel [1 pull requests]