openclaw - ✅(Solved) Fix WhatsApp: align multi-account `groups` inheritance with Telegram's isMultiAccount guard (breaking) [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#69874Fetched 2026-04-22 07:47:04
View on GitHub
Comments
0
Participants
1
Timeline
10
Reactions
0
Participants
Assignees
Timeline (top)
cross-referenced ×3labeled ×2referenced ×2assigned ×1

Normalize multi-account groups inheritance on the WhatsApp channel to match Telegram's existing behavior, so root-level channels.whatsapp.groups is not silently inherited by every account when more than one account is configured.

This is a behavior-preserving carve-out from #59553 (WhatsApp per-group systemPrompt): that PR shipped per-chat system prompts without changing the pre-existing inheritance model. PR author @Bluetegu explicitly flagged this design question on 2026-04-02 (comment) and asked maintainers to decide. The decision was deferred so #59553 could land without breaking any current users; this issue tracks the follow-up.

Root Cause

Normalize multi-account groups inheritance on the WhatsApp channel to match Telegram's existing behavior, so root-level channels.whatsapp.groups is not silently inherited by every account when more than one account is configured.

This is a behavior-preserving carve-out from #59553 (WhatsApp per-group systemPrompt): that PR shipped per-chat system prompts without changing the pre-existing inheritance model. PR author @Bluetegu explicitly flagged this design question on 2026-04-02 (comment) and asked maintainers to decide. The decision was deferred so #59553 could land without breaking any current users; this issue tracks the follow-up.

Fix Action

Fixed

PR fix notes

PR #69906: fix(whatsapp): guard multi-account groups inheritance (#69874)

Description (problem / solution / changelog)

Closes #69874.

Root cause

extensions/whatsapp/src/account-config.ts resolved every account via a flat { ...channelConfig, ...accountConfig } merge, with no awareness of how many accounts were configured. Root channels.whatsapp.groups therefore flowed into every account that did not explicitly set its own groups, regardless of accounts size. After #59553 added groups["*"].systemPrompt, that silent inheritance also fan-injected one account's wildcard system prompt into every other account's group sessions. Telegram already avoided this at the corresponding seam (mergeTelegramAccountConfig, #30673) via an isMultiAccount guard; the WhatsApp path was the only remaining divergence.

Fix

Mirror Telegram's guard in resolveMergedWhatsAppAccountConfig. After the existing flat merge, recompute groups with a three-level precedence:

  1. account-level groups (unchanged)
  2. accounts.default.groups shared defaults for named accounts (unchanged)
  3. channel-level channels.whatsapp.groups only when Object.keys(accounts).length <= 1 (new guard)

Behavior matrix:

ScenarioBeforeAfter
Single-account, no account-level groupsinherits rootinherits root (unchanged)
Single-account, has account-level groupsaccount replaces rootaccount replaces root (unchanged)
Multi-account, no account-level groups, no accounts.default.groupsinherits rootundefined (new)
Multi-account, accounts.default.groups setinherits via shared defaultsinherits via shared defaults (unchanged)
Multi-account, account-level groups setaccount replaces rootaccount replaces root (unchanged)

Per the issue's open sub-question #1, only groups is guarded — matching Telegram's scope exactly. direct, toolsBySender, and all other account-shaped fields are untouched.

Why this is safe

  • No default value changed. ResolvedWhatsAppAccount.groups was already | undefined, and single-account (including the overwhelmingly common zero-config default-only case) behavior is byte-identical.
  • Tighter, not looser. In multi-account setups the change removes silent inheritance, so any runtime check that keyed off account.groups now receives either the explicitly configured map or undefined; it never receives an account that was not in the intended scope.
  • Shared baseline preserved. Named accounts still pick up accounts.default.groups through resolveWhatsAppDefaultAccountSharedConfig, so existing multi-account configs that put their shared map under accounts.default keep working without change.
  • Mirrors a shipped, battle-tested guard. The logic is the same pattern as mergeTelegramAccountConfig's isMultiAccount branch, including the Object.keys(accounts).length > 1 heuristic.

Security / runtime controls unchanged

  • Sender authorization (groupPolicy, groupAllowFrom, dmPolicy, allowFrom, commands.ownerAllowFrom), DM pairing rules, action gates, and the command-auth path in inbound-policy.ts are all unchanged. The guard only narrows which groups map is associated with a given account; it does not soften any authorization check. No runtime-enforced policy is replaced by prompt text in this change, and no prompt text is elevated into a policy decision.
  • Wildcard group admission via groups["*"] in multi-account setups becomes opt-in per account instead of implicit across accounts. This is the intended effect and matches Telegram's long-standing behavior.
  • channels.whatsapp.direct inheritance is deliberately left as-is (matches Telegram's guard scope and the issue's stated preference).

Migration

Multi-account WhatsApp users who relied on root channels.whatsapp.groups being inherited by every account must now put the map under each account's config block (or under accounts.default to keep the shared-baseline behavior). A note was added to docs/channels/whatsapp.md and the "Unreleased / Changes" section of CHANGELOG.md.

Tests run

  • pnpm test:extension whatsapp → 63 files / 523 tests passing, including the seven new cases added in extensions/whatsapp/src/accounts.test.ts covering:
    • single-account inherits root groups
    • single-account account-level groups wins
    • multi-account named account with no own groupsundefined
    • multi-account default account with no own groupsundefined
    • multi-account account-level groups wins
    • multi-account accounts.default.groups shared defaults still flow to named accounts
    • multi-account root direct is intentionally not guarded
  • pnpm exec vitest run --project extension-telegram extensions/telegram/src/accounts.test.ts → 28/28 passing (sanity check on the mirrored Telegram path).
  • pnpm exec tsc --noEmit -p extensions/whatsapp/tsconfig.json → no new type errors introduced (verified by stashing the diff and rerunning; the three pre-existing errors on main are unrelated to this change and not touched).

Checklist

  • AI-assisted, fully tested locally against extension-whatsapp and the mirrored Telegram accounts suite
  • Docs updated (docs/channels/whatsapp.md)
  • Changelog updated (CHANGELOG.md Unreleased → Changes, marked breaking with migration guidance)
  • Bot review conversations will be addressed/resolved on the PR

Made with Cursor

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/channels/whatsapp.md (modified, +3/-2)
  • extensions/whatsapp/src/account-config.ts (modified, +14/-0)
  • extensions/whatsapp/src/accounts.test.ts (modified, +84/-0)

PR #70000: feat(whatsapp): suppress root groups inheritance in multi-account (#69874)

Description (problem / solution / changelog)

Summary

Closes #69874. Normalizes multi-account groups inheritance on the WhatsApp channel so root-level channels.whatsapp.groups is not silently fan-inherited to every account when more than one account is configured. This matches the existing Telegram guard in extensions/telegram/src/account-config.ts (mergeTelegramAccountConfig) and carves out the follow-up that @Bluetegu flagged in #59553 and maintainers deferred so that PR could land cleanly.

This matters because #59553 added per-group systemPrompt. Before that, silent root-level fan-out of groups was mostly benign (requireMention, tool policies). Now a root channels.whatsapp.groups["*"].systemPrompt: "You are Aria." gets injected into every account's group sessions with no opt-out short of defining an empty groups map per account.

Before / after

ScenarioTelegram groupsWhatsApp todayWhatsApp after this PR
Single-account, no account-level groupsinherits rootinherits rootinherits root (unchanged)
Single-account, has account-level groupsaccount replaces rootaccount replaces rootaccount replaces root (unchanged)
Multi-account, no account-level groupsundefined — root suppressedinherits root (discrepancy)undefined — root suppressed
Multi-account, has account-level groupsaccount replaces rootaccount replaces rootaccount replaces root (unchanged)

Why accept the break

  • Telegram parity: the same channel contract should behave the same way for multi-account setups; discrepancies here are a trap for users managing both bots.
  • #59553 made the existing silent fan-out dangerous via groups["*"].systemPrompt; a root-level persona bleeds into every account's group sessions by default.
  • #59553's author (@Bluetegu) explicitly flagged this design question and asked for a maintainer decision; the decision was deferred so #59553 could ship without breaking any current users. This PR is that follow-up.

Migration

Multi-account users who previously relied on implicit root-level groups inheritance must move the map into each account block explicitly. Two supported patterns:

  • Duplicate under each account: channels.whatsapp.accounts.<id>.groups = { ... }.
  • Share via accounts.default: channels.whatsapp.accounts.default.groups = { ... } — this still flows to named accounts because the accounts.default shared-defaults path is an explicit opt-in, distinct from implicit root inheritance.

Single-account users see no behavior change.

Open questions for maintainers

Two follow-ups the issue flagged; this PR stays minimal and matches Telegram exactly, so both are left open for maintainer decision:

  1. Extend the guard to direct? Telegram only guards groups, so this PR only guards groups. channels.whatsapp.direct has the same per-chat systemPrompt shape (added by #59553) and the same silent fan-out risk in multi-account configs. Matching Telegram's current behavior means leaving it alone here; the question is whether Telegram should gain a direct equivalent, or whether WhatsApp should extend the guard unilaterally.
  2. Other per-account-shaped maps? In extensions/whatsapp/src/account-config.ts / src/config/types.whatsapp.ts, the top-level fields that are Record<chatId, ...> shaped and have the same latent-inheritance concern are: groups (guarded here), direct, and dms. Nested under groups[*] there is also toolsBySender, which rides along with groups so it is covered transitively. None of the non-groups fields are touched by this PR.

Tests

Added three cases to extensions/whatsapp/src/accounts.test.ts that lock in the matrix:

  • Single-account, no account-level groups → inherits root (preservation).
  • Multi-account, no account-level groups → groups is undefined on the resolved account (new behavior; was broken before).
  • Multi-account, account defines groups → account replaces root (preservation).

The existing accounts.default shared-defaults tests still pass, confirming that explicit shared-defaults under accounts.default still flow to named accounts.

Closes #69874.

🤖 Generated with Claude Code

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/whatsapp/src/account-config.ts (modified, +12/-3)
  • extensions/whatsapp/src/accounts.test.ts (modified, +70/-0)

Code Example

const configuredAccountIds = Object.keys(cfg.channels?.whatsapp?.accounts ?? {});
const isMultiAccount = configuredAccountIds.length > 1;
const groups = account.groups ?? (isMultiAccount ? undefined : channelGroups);
const direct = account.direct ?? (isMultiAccount ? undefined : channelDirect);  // optional, see below
RAW_BUFFERClick to expand / collapse

Summary

Normalize multi-account groups inheritance on the WhatsApp channel to match Telegram's existing behavior, so root-level channels.whatsapp.groups is not silently inherited by every account when more than one account is configured.

This is a behavior-preserving carve-out from #59553 (WhatsApp per-group systemPrompt): that PR shipped per-chat system prompts without changing the pre-existing inheritance model. PR author @Bluetegu explicitly flagged this design question on 2026-04-02 (comment) and asked maintainers to decide. The decision was deferred so #59553 could land without breaking any current users; this issue tracks the follow-up.

Current state (after #59553)

extensions/whatsapp/src/account-config.tsresolveMergedWhatsAppAccountConfig applies a flat { ...base, ...accountConfig } merge with no multi-account guard. Root groups is inherited by every account that defines no groups of its own, regardless of how many accounts are configured.

ScenarioTelegram groupsWhatsApp groups
Single-account, no account-level groupsinherits rootinherits root
Single-account, has account-level groupsaccount replaces rootaccount replaces root
Multi-account, no account-level groupsundefined — root suppressedinherits root ← discrepancy
Multi-account, has account-level groupsaccount replaces rootaccount replaces root

Why it matters more now

Before #59553 the inherited keys were requireMention, allowFrom, tool policies — surprising but mostly benign. After #59553, groups["*"].systemPrompt also gets silently fan-inherited across every account, including groups that account never joined. A root groups["*"].systemPrompt: "You are Aria." injects that prompt into every account's group sessions with no opt-out short of defining an empty groups map per account.

Telegram's guard exists for a related reason (#30673): a bot that isn't a member of a configured group will fail when messages arrive for it, and the failure can disrupt delivery for all accounts sharing the same runtime. Both surfaces benefit from the same membership-isolation principle.

Proposal

Add the same isMultiAccount guard to resolveMergedWhatsAppAccountConfig (or to resolveWhatsAppAccount if that's the cleaner seam), mirroring extensions/telegram/src/accounts.ts:

const configuredAccountIds = Object.keys(cfg.channels?.whatsapp?.accounts ?? {});
const isMultiAccount = configuredAccountIds.length > 1;
const groups = account.groups ?? (isMultiAccount ? undefined : channelGroups);
const direct = account.direct ?? (isMultiAccount ? undefined : channelDirect);  // optional, see below

Keep single-account behavior unchanged (root groups still inherited when the account defines none). Only suppress inheritance when accounts has more than one entry.

Open sub-questions for the implementer

  1. direct as well? Telegram only guards groups (membership is group-specific). The same principle plausibly applies to direct, since account A's allowlisted DMs aren't account B's — but the current Telegram behavior doesn't suppress direct, so matching Telegram exactly means only guarding groups. Decide and document.
  2. toolsBySender / other per-account-shaped maps — audit whether they have the same latent silent-inheritance risk and fold into scope if so.

Migration guidance (required in release notes)

Multi-account WhatsApp users who rely on root groups being inherited by every account must move those entries under each account's config block explicitly. Example migration already written up in https://github.com/openclaw/openclaw/pull/59553#issuecomment-4179853515 — reuse.

Tests

  • New test in extensions/whatsapp/src/accounts.test.ts:
    • Single-account, no account-level groups: inherits root. (existing behavior)
    • Multi-account, no account-level groups: groups is undefined on the resolved account.
    • Multi-account, account defines groups: account replaces root. (existing behavior)
  • Mirror any equivalent Telegram regression tests for isMultiAccount.

References

  • #59553 — original WhatsApp systemPrompt PR (merged 2026-04-21 in 08bc16853e); Ron's maintainer-question comment is https://github.com/openclaw/openclaw/pull/59553#issuecomment-4179853515
  • Telegram implementation: extensions/telegram/src/accounts.tsmergeTelegramAccountConfig, and issue #30673 for the motivating incident.
  • Closes #7011 was addressed by #59553; this issue is the cleanup tail.

Labels

Breaking change, channel: whatsapp-web.

extent analysis

TL;DR

Add an isMultiAccount guard to resolveMergedWhatsAppAccountConfig to prevent silent inheritance of root-level groups in multi-account WhatsApp configurations.

Guidance

  • Identify the resolveMergedWhatsAppAccountConfig function in extensions/whatsapp/src/account-config.ts and add a check for isMultiAccount to conditionally suppress inheritance of root groups.
  • Determine whether to apply the same guard to direct and other per-account-shaped maps, such as toolsBySender, to prevent similar silent-inheritance risks.
  • Update tests in extensions/whatsapp/src/accounts.test.ts to cover single-account and multi-account scenarios with and without account-level groups.
  • Consider mirroring Telegram regression tests for isMultiAccount to ensure consistent behavior.

Example

const configuredAccountIds = Object.keys(cfg.channels?.whatsapp?.accounts ?? {});
const isMultiAccount = configuredAccountIds.length > 1;
const groups = account.groups ?? (isMultiAccount ? undefined : channelGroups);

Notes

The proposed change may require updates to release notes and migration guidance for multi-account WhatsApp users who rely on root groups being inherited by every account.

Recommendation

Apply the workaround by adding the isMultiAccount guard to resolveMergedWhatsAppAccountConfig, as it addresses the specific issue of silent inheritance of root-level groups in multi-account WhatsApp configurations.

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