Cap Discord slash command registration and sync - #28800
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for chasing this. The underlying bug still exists on current main, but the branch needs salvage work before it is safe to land.
Problems
- Current main moved Discord into the bundled plugin:
gateway/platforms/discord.pyno longer exists, and the active code isplugins/platforms/discord/adapter.py(git ls-tree -r origin/mainverified this). The PR is currently merge-conflicting. - The create-before-delete bug is real on current main:
_safe_sync_slash_commands()still upserts missing commands before deleting stale commands atplugins/platforms/discord/adapter.py:1469-1494. - The cap bug is also real:
_register_slash_commands()still auto-adds everyCOMMAND_REGISTRYcommand and plugin command before unconditional/skillregistration atplugins/platforms/discord/adapter.py:3531-3590. - The new
DISCORD_GLOBAL_COMMAND_LIMITknob is a non-secret behavioral setting;AGENTS.md:102-104says those should live inconfig.yaml, not as new env vars.
Suggested changes
- Port the fix onto
plugins/platforms/discord/adapter.pyand keep deletes routed through the existingmutate()pacing wrapper. - Prefer an explicit priority budget: preserve fixed core commands and
/skill, then fill optional registry/plugin commands. - Update
website/docs/user-guide/messaging/discord.md:581-588, which still describes one top-level command per skill.
Automated hermes-sweeper review.
| """Return the hard Discord global application-command budget. | ||
|
|
||
| Discord caps global application commands at 100 per app. Keep an | ||
| env override for tests and emergency local mitigation, but never let |
There was a problem hiding this comment.
This is a non-secret behavioral setting, and AGENTS.md asks new behavioral knobs to go through config.yaml rather than another env var. If a lower cap is needed for tests, monkeypatching the helper keeps the production surface smaller.
| "before sync", | ||
| self.name, | ||
| len(desired_payloads), | ||
| command_limit, |
There was a problem hiding this comment.
This fallback trims by whatever order tree.get_commands() returns. If we hit this path, we should preserve an explicit priority set such as fixed core commands and /skill, then drop optional registry/plugin commands deterministically.
|
Closing in favor of #46078, which fixes the same Discord error-30032 root cause (capping the desired slash-command set at 100) and has merged. This PR targets |
Summary
Fixes Hermes Discord gateway startup failures caused by Discord's global application command cap.
Hermes can currently build more top-level slash commands than Discord allows, especially when combining hardcoded commands,
COMMAND_REGISTRYauto-registration, plugin commands, and the/skillcommand. When the app is already at the command cap, the safe sync path can also fail because it creates or updates desired commands before deleting stale commands.This can surface as:
API call failed after 3 retries: Connection errorAPI failed after 3 retries — Connection errorAuxiliary title generation failed: Connection error30032during command syncWhat changed
DISCORD_GLOBAL_COMMAND_LIMIT.COMMAND_REGISTRYslash commands./skillcommand so the skill catalog does not expand into many top-level commands./skillduring defensive trimming when possible.Local mitigation attempted for Zeus
Before this code fix, Zeus was mitigated locally by disabling Discord command sync:
DISCORD_COMMAND_SYNC_POLICY=offtoai.hermes.gateway-zeusLaunchAgent.launchctl kickstart; this restarted the service but did not reload the new environment.launchctl bootoutandlaunchctl bootstrapto reload the plist.launchctl print gui/502/ai.hermes.gateway-zeusshowsDISCORD_COMMAND_SYNC_POLICY => off.Skipping Discord slash command sync (policy=off).This stops Zeus from repeatedly hitting Discord sync failures while the upstream fix is reviewed.
Tests
Result:
Also ran:
Result: clean.
Notes
This change does not immediately mutate the live Discord app while sync is disabled. After this lands, Zeus should run one controlled sync with the patched code to prune stale commands and bring the live command set back under Discord's limit.