hermes - 💡(How to fix) Fix [Bug]: Discord View classes never defined — lazy-install leaves discord=None at class definition time

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

ERROR gateway.platforms.discord: Failed to define Discord View classes: 'NoneType' object has no attribute 'ui' File "gateway/platforms/discord.py", line 4920, in <module> class ExecApprovalView(discord.ui.View): AttributeError: 'NoneType' object has no attribute 'ui'

Root Cause

The root cause is a module-load ordering issue in gateway/platforms/discord.py:

Fix Action

Workaround

A startup patch that performs the above transformation on discord.py before the gateway starts. Happy to share the full patch script if helpful.

Code Example

if DISCORD_AVAILABLE:
       class ExecApprovalView(discord.ui.View):
           ...

---

WARNING gateway.run: Button-based approval failed (send returned error), falling back to text: name 'ExecApprovalView' is not defined

---

ERROR gateway.platforms.discord: Failed to define Discord View classes: 'NoneType' object has no attribute 'ui'
  File "gateway/platforms/discord.py", line 4920, in <module>
    class ExecApprovalView(discord.ui.View):
AttributeError: 'NoneType' object has no attribute 'ui'

---

def _define_discord_views():
    global ExecApprovalView, SlashConfirmView, UpdatePromptView, ModelPickerView, ClarifyChoiceView
    if not DISCORD_AVAILABLE or discord is None:
        return
    # ... existing class definitions (unchanged) ...

# At module level (works if discord is pre-installed):
_define_discord_views()

---

DISCORD_AVAILABLE = True
    _define_discord_views()  # define View classes after lazy install
    return True
RAW_BUFFERClick to expand / collapse

Bug Description

All Discord button-based UIs (exec approvals, slash-command confirmations like /new and /reset, clarify choices, model picker) silently fall back to plain text in Docker gateway deployments.

The root cause is a module-load ordering issue in gateway/platforms/discord.py:

What happens

  1. Module load — top of discord.py runs import discord, which fails (discord.py is lazily installed). The except branch sets discord = None, DISCORD_AVAILABLE = False.

  2. View class definitions — near the bottom of the file, all discord.ui.View subclasses (ExecApprovalView, SlashConfirmView, UpdatePromptView, ModelPickerView, ClarifyChoiceView) are gated behind:

    if DISCORD_AVAILABLE:
        class ExecApprovalView(discord.ui.View):
            ...

    Since DISCORD_AVAILABLE is False at this point, the entire block is skipped. The classes are never defined.

  3. Lazy installcheck_discord_requirements() is called from run.py:_create_adapter() after the module has fully loaded. It successfully installs discord.py via tools.lazy_deps.ensure(), rebinds discord = <module> and DISCORD_AVAILABLE = True in the module globals. But the if DISCORD_AVAILABLE: block already executed (and was skipped) during step 2 — it never re-runs.

  4. Runtimesend_exec_approval() and send_slash_confirm() reference ExecApprovalView / SlashConfirmView by name. They get NameError: name 'ExecApprovalView' is not defined, which is caught and silently falls through to the text-only fallback path.

Evidence from logs

WARNING gateway.run: Button-based approval failed (send returned error), falling back to text: name 'ExecApprovalView' is not defined

And when patching the if DISCORD_AVAILABLE: guard to try: (to surface the real error):

ERROR gateway.platforms.discord: Failed to define Discord View classes: 'NoneType' object has no attribute 'ui'
  File "gateway/platforms/discord.py", line 4920, in <module>
    class ExecApprovalView(discord.ui.View):
AttributeError: 'NoneType' object has no attribute 'ui'

Expected behavior

Discord buttons should render for:

  • Exec approval prompts (Allow Once / Allow Session / Always Allow / Deny)
  • Slash-command confirmations (/new, /reset, /undo — Approve Once / Always Approve / Cancel)
  • Clarify choice prompts
  • Model picker

Actual behavior

All of the above fall back to plain text with "Text fallback: reply /approve, /always, or /cancel."

Environment

  • Image: nousresearch/hermes-agent:latest (pulled May 16, 2025)
  • Deployment: Kubernetes, Docker gateway mode (hermes gateway run)
  • discord.py version: 2.7.1 (lazily installed at runtime)
  • Python: 3.13

Suggested fix

Wrap the View class definitions in a callable function and invoke it from check_discord_requirements() after the lazy install completes:

def _define_discord_views():
    global ExecApprovalView, SlashConfirmView, UpdatePromptView, ModelPickerView, ClarifyChoiceView
    if not DISCORD_AVAILABLE or discord is None:
        return
    # ... existing class definitions (unchanged) ...

# At module level (works if discord is pre-installed):
_define_discord_views()

And in check_discord_requirements(), after DISCORD_AVAILABLE = True:

    DISCORD_AVAILABLE = True
    _define_discord_views()  # define View classes after lazy install
    return True

This ensures the classes are defined regardless of whether discord is available at import time or lazily installed later.

Workaround

A startup patch that performs the above transformation on discord.py before the gateway starts. Happy to share the full patch script if helpful.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Discord buttons should render for:

  • Exec approval prompts (Allow Once / Allow Session / Always Allow / Deny)
  • Slash-command confirmations (/new, /reset, /undo — Approve Once / Always Approve / Cancel)
  • Clarify choice prompts
  • Model picker

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: Discord View classes never defined — lazy-install leaves discord=None at class definition time