Skip to content

Cap Discord slash command registration and sync - #28800

Closed
mechramc wants to merge 1 commit into
NousResearch:mainfrom
mechramc:codex/discord-command-cap
Closed

Cap Discord slash command registration and sync#28800
mechramc wants to merge 1 commit into
NousResearch:mainfrom
mechramc:codex/discord-command-cap

Conversation

@mechramc

Copy link
Copy Markdown

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_REGISTRY auto-registration, plugin commands, and the /skill command. 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 error
  • API failed after 3 retries — Connection error
  • Auxiliary title generation failed: Connection error
  • Discord error 30032 during command sync

What changed

  • Added a Discord global command budget helper, defaulting to 100 and clampable by DISCORD_GLOBAL_COMMAND_LIMIT.
  • Capped optional auto-registered COMMAND_REGISTRY slash commands.
  • Capped optional plugin slash commands.
  • Reserved space for the scalable /skill command so the skill catalog does not expand into many top-level commands.
  • Added defensive trimming before sync if the desired command payload still exceeds the limit.
  • Preserved /skill during defensive trimming when possible.
  • Changed safe sync ordering to delete obsolete Discord commands before creating new ones, so apps already at the cap can make room before replacement commands are upserted.

Local mitigation attempted for Zeus

Before this code fix, Zeus was mitigated locally by disabling Discord command sync:

  • Added DISCORD_COMMAND_SYNC_POLICY=off to ai.hermes.gateway-zeus LaunchAgent.
  • Tried launchctl kickstart; this restarted the service but did not reload the new environment.
  • Performed full launchctl bootout and launchctl bootstrap to reload the plist.
  • Verified launchctl print gui/502/ai.hermes.gateway-zeus shows DISCORD_COMMAND_SYNC_POLICY => off.
  • Verified the gateway log reports Skipping Discord slash command sync (policy=off).

This stops Zeus from repeatedly hitting Discord sync failures while the upstream fix is reviewed.

Tests

/Users/murai-labs/.hermes/hermes-agent/venv/bin/python -m pytest tests/gateway/test_discord_connect.py tests/gateway/test_discord_slash_commands.py -q

Result:

51 passed

Also ran:

git diff --check

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.

@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/gateway Gateway runner, session dispatch, delivery platform/discord Discord bot adapter labels May 19, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.py no longer exists, and the active code is plugins/platforms/discord/adapter.py (git ls-tree -r origin/main verified 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 at plugins/platforms/discord/adapter.py:1469-1494.
  • The cap bug is also real: _register_slash_commands() still auto-adds every COMMAND_REGISTRY command and plugin command before unconditional /skill registration at plugins/platforms/discord/adapter.py:3531-3590.
  • The new DISCORD_GLOBAL_COMMAND_LIMIT knob is a non-secret behavioral setting; AGENTS.md:102-104 says those should live in config.yaml, not as new env vars.

Suggested changes

  • Port the fix onto plugins/platforms/discord/adapter.py and keep deletes routed through the existing mutate() 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

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 gateway/platforms/discord.py, which has since moved to plugins/platforms/discord/adapter.py — it no longer applies cleanly (currently CONFLICTING). #46078 also keeps the cap a constant rather than introducing a DISCORD_GLOBAL_COMMAND_LIMIT env var, in line with the project's config conventions. Thanks for the thorough investigation and the Zeus mitigation notes — they corroborated the root cause.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround platform/discord Discord bot adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants