#17579: fix(slack): prevent Zod default groupPolicy from breaking multi-account config
stale
size: XS
Cluster:
Signal and Discord Fixes
## Summary
- **Moves `.default("allowlist")` from `SlackAccountSchema` to `SlackConfigSchema`** so Zod only injects the default at the top-level config, not into individual account entries.
- Fixes multi-account Slack configs silently dropping all channel messages (DMs still worked, @mentions in channels did not).
## Root Cause
`SlackAccountSchema` has `groupPolicy: GroupPolicySchema.optional().default("allowlist")`. When Zod parses an account entry like `{ botToken: "...", appToken: "..." }`, it injects `groupPolicy: "allowlist"` into the output — even though the user never specified it.
`mergeSlackAccountConfig` then does `{ ...base, ...account }`, so the account's phantom `"allowlist"` overrides the user's top-level `groupPolicy: "open"`. With `"allowlist"` active and no channels explicitly listed, `isSlackChannelAllowedByPolicy` returns `false` for every channel message.
This affects **any** config that uses the `channels.slack.accounts` object, even with a single account.
The developer's intent was probably "default to secure" — which is reasonable. The mistake was putting that default on the account schema instead of the top-level schema. The right place for "default to secure if nobody says anything" is SlackConfigSchema, because that's the one that represents the user's actual top-level config. Individual account entries are meant to inherit from that, and Zod defaults break the inheritance by filling in keys that should have stayed undefined.
## The Fix
```diff
- // SlackAccountSchema
- groupPolicy: GroupPolicySchema.optional().default("allowlist"),
+ // SlackAccountSchema — no default, so accounts don't poison the merge
+ groupPolicy: GroupPolicySchema.optional(),
// SlackConfigSchema.safeExtend — default lives here now
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
```
## Test Plan
- [x] All 149 existing Slack tests pass (`pnpm test -- --grep slack`)
- [x] Live-tested on Raspberry Pi running OpenClaw v2026.2.14 with two Slack bot accounts in socket mode
- [x] Confirmed DMs work for both accounts
- [x] Confirmed @mentions in channels work for both accounts
- [x] Confirmed non-mentioned messages in channels are correctly skipped
- [x] Single-account top-level config (no `accounts` object) continues to work as before
Fixes #17507
Related: #5626, #16522
cc @thewilloftheshadow (Slack subsystem) @gumadeiras (multi-agent)
---
🤖 **AI-assisted:** This bug was diagnosed and fixed by Claude Opus 4.6 (Anthropic) in a live demo session with @ZetiMente. Fully tested on a live instance.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes a bug where Zod's `.default("allowlist")` on `SlackAccountSchema.groupPolicy` caused a phantom default to be injected into every parsed account entry, overriding the user's top-level `groupPolicy` during the shallow merge (`{ ...base, ...account }`) in `mergeSlackAccountConfig`. This silently broke channel message delivery for multi-account Slack configs.
- Removed `.default("allowlist")` from `SlackAccountSchema` so account entries no longer get a phantom `groupPolicy` injected
- Added `.default("allowlist")` to `SlackConfigSchema` via `safeExtend` so the secure default still applies at the top-level config
- **The same bug pattern exists in other providers** (Discord, Telegram, Signal, IRC, iMessage, BlueBubbles) — all have `.default("allowlist")` on their account schemas and use the same shallow merge, but were not addressed in this PR
<h3>Confidence Score: 4/5</h3>
- The Slack fix is correct and safe to merge, though the same bug exists in other providers.
- The change is minimal, well-reasoned, and correctly fixes the Slack-specific issue. The author reports all 149 existing tests pass and the fix was live-tested. Deducting a point because the same Zod default-on-account-schema bug is present in 6 other providers (Discord, Telegram, Signal, IRC, iMessage, BlueBubbles) that use the identical merge pattern, and this PR does not address them.
- Other account schemas in `src/config/zod-schema.providers-core.ts` (DiscordAccountSchema line 275, TelegramAccountSchemaBase line 108, SignalAccountSchemaBase line 657, IrcAccountSchemaBase line 745, IMessageAccountSchemaBase line 805, BlueBubblesAccountSchemaBase line 897) have the same latent bug.
<sub>Last reviewed commit: fc243e3</sub>
<!-- greptile_other_comments_section -->
<sub>(1/5) You can manually trigger the agent by mentioning @greptileai in a comment!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#22476: fix(slack): add streamMode to Zod schema and support "off" mode
by ms-ponyo · 2026-02-21
80.6%
#19615: fix(discord): include default account when sub-accounts are configured
by prue-starfield · 2026-02-18
78.3%
#18656: fix(signal): add missing groups field to Signal channel schema (#18...
by awkoy · 2026-02-16
77.4%
#10943: fix(config): resolve Control UI "Unsupported schema node" for confi...
by kraftbj · 2026-02-07
77.2%
#8024: fix(slack): resolve channel names via directory for cross-account m...
by emma-digital-assistant · 2026-02-03
77.0%
#19435: fix(slack): properly handle group DM (MPIM) events
by Utkarshbhimte · 2026-02-17
76.5%
#19567: Fix: tighten Slack multi-account event filtering via api_app_id
by TARS-Nolan · 2026-02-17
76.5%
#22557: fix(discord): coerce exec approval approver IDs to string to preven...
by zwffff · 2026-02-21
75.9%
#23320: fix(slack): respect replyToMode when incomingThreadTs is auto-created
by dorukardahan · 2026-02-22
75.8%
#15864: feat: add deliverOnlyToolMessages config for clean messaging channe...
by gandalf-the-engineer · 2026-02-14
75.4%