#19615: fix(discord): include default account when sub-accounts are configured
channel: discord
size: S
Cluster:
Signal and Discord Fixes
## Summary
- **Problem:** When named sub-accounts are added under `channels.discord.accounts`, the implicit default account is silently dropped — the primary Discord bot stops connecting on gateway restart while other bots work fine.
- **Why it matters:** Users with multi-bot setups lose their primary bot every time the gateway restarts, with no error or warning.
- **What changed:** (1) `createAccountListHelpers` now merges bound account IDs from `bindings` config alongside configured accounts, matching the Telegram implementation. (2) Discord's `listDiscordAccountIds` ensures `DEFAULT_ACCOUNT_ID` is included when a top-level discord token is present.
- **What did NOT change (scope boundary):** Token resolution, account config merging, the Telegram implementation (already correct), and all other channel account listing. No config format changes.
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [x] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
N/A — recurring operational issue observed across multiple gateway restarts.
## User-visible / Behavior Changes
Discord bots configured via a top-level `channels.discord.token` will no longer disappear when named sub-accounts are also configured under `channels.discord.accounts`. Previously, only the sub-accounts would start; now the default account starts alongside them.
## 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`
## Repro + Verification
### Environment
- OS: macOS (Darwin 25.2.0)
- Runtime/container: Node 22
- Integration/channel: Discord
- Relevant config (redacted):
```json
{
"channels": {
"discord": {
"token": "<top-level-default-bot-token>",
"accounts": {
"bot-b": { "token": "<bot-b-token>" },
"bot-c": { "token": "<bot-c-token>" }
}
}
}
}
```
### Steps
1. Configure a Discord channel with a top-level token AND named sub-accounts under `channels.discord.accounts`
2. Start or restart the gateway
3. Observe gateway logs for `[discord] [default] starting provider`
### Expected
All three providers start: `[default]`, `[bot-b]`, `[bot-c]`
### Actual (before fix)
Only `[bot-b]` and `[bot-c]` start. `[default]` is missing — the primary bot never connects.
## Evidence
- [x] Trace/log snippets
**Before fix:**
```
[discord] [bot-b] starting provider (@BotB)
[discord] [bot-c] starting provider (@BotC)
# [default] is MISSING — no "starting provider" line
```
**After fix:**
```
[discord] [bot-b] starting provider (@BotB)
[discord] [bot-c] starting provider (@BotC)
[discord] [default] starting provider (@DefaultBot)
logged in to discord as <id-1>
logged in to discord as <id-2>
logged in to discord as <id-3>
```
- [x] Failing test/log before + passing after
New test added in `account-helpers.test.ts`: "includes bound account ids from bindings" — verifies that accounts referenced in bindings config are included in the account list.
## Human Verification (required)
- **Verified scenarios:** Gateway restart with 3 Discord accounts (1 default + 2 named). All three bots connected and appeared online in Discord after the fix.
- **Edge cases checked:** Empty accounts config (returns `["default"]`), accounts with no top-level token (default not injected), accounts that already include `"default"` explicitly (no duplicate).
- **What I did NOT verify:** Other channels using `createAccountListHelpers` (Signal, iMessage, Slack, WhatsApp) — the shared helper change adds bound accounts for all channels, but I only tested Discord end-to-end.
## Compatibility / Migration
- Backward compatible? `Yes`
- Config/env changes? `No`
- Migration needed? `No`
## Failure Recovery (if this breaks)
- **How to revert:** Revert commit and rebuild. The only behavioral change is that more accounts appear in `listAccountIds` results — reverting returns to the previous (broken) behavior where sub-accounts hide the default.
- **Known bad symptoms:** If somehow the default account is injected when it shouldn't be (no top-level token), it would fail to connect with a `tokenSource: "none"` and be filtered out by `listEnabledDiscordAccounts`. No crash risk.
## Risks and Mitigations
- **Risk:** The shared `account-helpers` change adds `listBoundAccountIds` for all channels, not just Discord. Channels that previously didn't see bound accounts in their account list now will.
- **Mitigation:** This matches the Telegram behavior (which already includes bound accounts) and is the correct semantic — bindings reference accounts that should exist. All existing tests pass (332 tests across discord/routing/account-helpers).
AI-assisted (Claude Opus 4.6). Fully tested end-to-end on a live multi-account Discord gateway.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes the default Discord account being silently dropped when named sub-accounts are configured under `channels.discord.accounts`. Two targeted changes address this:
- **Shared helper (`account-helpers.ts`)**: `listAccountIds` now merges bound account IDs from `bindings` config alongside explicitly configured accounts, matching the pattern already used by the Telegram implementation in `src/telegram/accounts.ts`.
- **Discord-specific (`discord/accounts.ts`)**: `listDiscordAccountIds` is promoted from a simple re-export to a wrapper function that injects `DEFAULT_ACCOUNT_ID` when a top-level discord token is present but "default" isn't explicitly listed. Correctly guards against duplicates and uses `resolveDiscordToken` to verify a token actually exists before injecting.
**Broader impact**: The shared helper change also adds bound-account awareness to Slack, Signal, WhatsApp, and iMessage (which re-export `listAccountIds` directly). This is consistent with the Telegram precedent and semantically correct—bindings reference accounts that should exist. Accounts without valid tokens/credentials are filtered out downstream by each channel's `listEnabled*Accounts` function.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge — it fixes a clear operational bug with minimal, well-scoped changes that align with existing patterns.
- The changes are small, well-tested, and follow the established Telegram pattern. The shared helper change is additive (new accounts may appear in lists but are filtered downstream by each channel's enabled/token checks). No config format changes, no new network calls, no security surface changes. Edge cases (empty accounts, no top-level token, duplicate default) are handled correctly. The new test covers the key behavioral change.
- No files require special attention.
<sub>Last reviewed commit: 9fbb464</sub>
<!-- greptile_other_comments_section -->
<sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#16736: fix: stagger multi-account channel startup to avoid Discord rate li...
by rm289 · 2026-02-15
86.3%
#17395: fix(discord): default dm config for sub-accounts to prevent silent ...
by deggertsen · 2026-02-15
83.1%
#14359: fix: prefer named Telegram account over orphan 'default' in binding...
by itsGustav · 2026-02-12
82.0%
#13652: fix(discord): ensure default bot loads when multiple accounts are con…
by metav · 2026-02-10
80.0%
#22557: fix(discord): coerce exec approval approver IDs to string to preven...
by zwffff · 2026-02-21
79.8%
#22611: fix(discord): allow messages from other instance bots in multi-acco...
by dae-sun · 2026-02-21
79.4%
#22965: fix(discord): pass accountId to ack reaction calls for named accounts
by arosstale · 2026-02-21
79.4%
#23238: fix(telegram): account named "default" silently breaks inbound polling
by anillBhoi · 2026-02-22
79.2%
#20967: fix(discord): report connected state so health-monitor can restart ...
by who96 · 2026-02-19
78.8%
#17579: fix(slack): prevent Zod default groupPolicy from breaking multi-acc...
by ZetiMente · 2026-02-15
78.3%