← Back to PRs

#22557: fix(discord): coerce exec approval approver IDs to string to prevent 64-bit truncation (#22437)

by zwffff open 2026-02-21 09:07 View on GitHub →
commands agents size: M
## Summary Describe the problem and fix in 2–5 bullets: - **Problem:** Discord user IDs in `channels.discord.execApprovals.approvers` (and other Discord ID list fields like `allowFrom`, guild/channel users and roles) were accepted or persisted as numbers. JavaScript number precision is limited to 2^53; 64-bit Discord snowflake IDs get truncated so the last digits become `00`, so approval requests never match the user. - **Why it matters:** Users cannot receive exec approval requests in Discord when IDs are set via CLI or config and end up stored as numbers; they must manually edit the config file with quoted strings and often need multiple edits. - **What changed:** (1) `DiscordIdSchema` now accepts string or number and **transforms** to string so parsed config always has string IDs and we never persist numbers. (2) When reading config, resolved config is normalized so all Discord ID list fields are string arrays before merge/write, so saved config does not re-persist numbers. (3) Doctor warning for numeric Discord IDs updated to recommend quoted strings for IDs above 2^53. - **What did NOT change:** Config that already uses quoted string IDs; doctor repair behavior; exec-approvals runtime logic. No change to other channels or config schema. ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [ ] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [x] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Closes #22437 - Related # ## User-visible / Behavior Changes - Config with **numeric** Discord IDs (e.g. `approvers: [123456789012345678]`) is now **accepted** and coerced to strings at parse time; approval delivery works for those IDs. - Config file written after load/set/patch stores Discord ID list values as **strings** (no more numeric truncation on save). - Doctor warning for numeric Discord IDs now recommends quoted strings for IDs above 2^53; `openclaw doctor --fix` still converts numeric entries to strings. ## 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 26.2 (from issue) - Runtime/container: Node 22+, npm global - Model/provider: N/A - Integration/channel: Discord - Relevant config (redacted): `channels.discord.execApprovals.enabled: true`, `approvers: [<64-bit Discord user ID>]` (previously written as number or set via CLI) ### Steps 1. Set `channels.discord.execApprovals.approvers` to a Discord user ID (e.g. via `openclaw config set` or edit config with unquoted number). 2. Trigger an exec approval that should be sent to that user in Discord. ### Expected - Approval request is delivered to the configured Discord user; ID is not truncated (last digits not `00`). ### Actual - Before fix: ID truncated; approval not received or wrong user. After fix: ID preserved as string; delivery works. ## Evidence Attach at least one: - [x] Failing test/log before + passing after: `src/config/config.discord.test.ts` — previously expected validation to reject numeric IDs; now expects acceptance and coercion to strings. `src/config/normalize-discord-ids.test.ts` added for resolved-config normalization. - [ ] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) ## Human Verification (required) - **Verified scenarios:** Unit tests for schema coercion and for `normalizeDiscordIdListsInResolved`; config test suite (`pnpm test -- src/config --run`) and Discord exec-approvals tests pass; `pnpm build && pnpm check` pass. - **Edge cases checked:** Numeric vs string entries in allowFrom, execApprovals.approvers, guild/channel users/roles; top-level discord and `accounts.*` scopes. - **What you did not verify:** Live Discord channel with real 64-bit user ID end-to-end; doctor --fix e2e (excluded by default test filter). ## Compatibility / Migration - Backward compatible? **Yes** - Config/env changes? **No** (no new keys or env vars) - Migration needed? **No** - If yes, exact upgrade steps: N/A ## Failure Recovery (if this breaks) - **How to disable/revert this change quickly:** Revert this PR; configs with numeric IDs will again fail validation or truncate on write. - **Files/config to restore:** `src/config/zod-schema.providers-core.ts`, `src/config/io.ts`, `src/config/normalize-discord-ids.ts`, `src/config/normalize-discord-ids.test.ts`, `src/commands/doctor-config-flow.ts`, `src/config/config.discord.test.ts`. - **Known bad symptoms reviewers should watch for:** None expected; coercion is additive. ## Risks and Mitigations - **Risk:** Config files that already contained truncated numeric IDs (e.g. `...00`) remain truncated after coercion to string; we cannot recover lost digits. - **Mitigation:** Document that for IDs > 2^53 users should use quoted strings in config; doctor warning suggests `openclaw doctor --fix` and explains precision. - **Risk:** None others identified. <!-- greptile_comment --> <h3>Greptile Summary</h3> Prevents 64-bit Discord snowflake ID truncation by coercing numeric IDs to strings at schema parse time and normalizing resolved config before merge/write. The fix addresses the root cause where JavaScript's 2^53 number precision limit corrupted Discord user IDs ending in `00`, preventing exec approval delivery. Schema now accepts number or string and transforms to string; config I/O normalizes all Discord ID list fields (allowFrom, execApprovals.approvers, guild/channel users/roles, groupChannels) so saved configs don't re-persist truncated numbers. - Modified `DiscordIdSchema` from `.refine()` validation (rejecting numbers) to `.transform()` coercion (accepting and converting) - Added `normalizeDiscordIdListsInResolved()` to convert numeric entries in all Discord ID list fields across top-level and account scopes - Updated doctor warning message to recommend quoted strings for IDs above 2^53 - Tests verify schema coercion and config normalization work correctly The PR branch includes unrelated commits (reasoning defaults, iOS changes, docs) but the HEAD commit `659dfae9` focuses solely on the Discord ID fix. <h3>Confidence Score: 4/5</h3> - Safe to merge with minor test coverage gap - The core fix is sound: schema transformation prevents truncation at parse time, and config normalization prevents re-persisting numbers. Tests verify both mechanisms. Minor test coverage gap where not all ID list fields are verified in assertions, but the normalization logic comprehensively handles all fields. No security concerns, backward compatible, and addresses a real precision bug. - No files require special attention <sub>Last reviewed commit: 659dfae</sub> <!-- greptile_other_comments_section --> <sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub> <!-- /greptile_comment -->

Most Similar PRs