hermes - ✅(Solved) Fix Approval flow can return 'User denied' when no prompt is delivered [1 pull requests, 1 participants]

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…
GitHub stats
NousResearch/hermes-agent#22992Fetched 2026-05-11 03:31:51
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
labeled ×3cross-referenced ×1

GitHub write actions can be blocked with:

BLOCKED: User denied. Do NOT retry.

in sessions where no approval prompt is visibly delivered to the user.

Error Message

  • Attempting external side-effect actions (e.g., posting GitHub issue comments) is blocked.
  • Tool returns: BLOCKED: User denied. Do NOT retry.
  • User reports they never received approve/deny prompt.

Root Cause

GitHub write actions can be blocked with:

BLOCKED: User denied. Do NOT retry.

in sessions where no approval prompt is visibly delivered to the user.

Fix Action

Fixed

PR fix notes

PR #23010: fix(approval): differentiate "callback unavailable" from "denied by user" in gateway approval flow

Description (problem / solution / changelog)

Summary

When unregister_gateway_notify() is called during gateway session teardown while an approval prompt is pending, the approval entry's event is set without a user decision (entry.result remains None). The previous code reported this as "denied by user", which is misleading — the user never denied anything.

The same logic also collapsed timeout and teardown into a single ternary, making both produce the same "denied by user" message.

Root cause

In tools/approval.py, check_all_command_guards() (line 929):

if not resolved or choice is None or choice == "deny":
    reason = "timed out" if not resolved else "denied by user"

When unregister_gateway_notify() fires (e.g. agent run interrupted), it sets entry.event without setting entry.result:

for entry in entries:
    entry.event.set()  # wakes waiting thread, but result stays None

The waiting thread sees resolved=True + choice=None, falls into the else branch, and reports "denied by user".

Fix

Differentiate all three conditions:

ConditionresolvedchoiceOld messageNew message
TimeoutFalseNone"timed out""timed out"
Teardown/unregisterTrueNone"denied by user""approval callback unavailable"
Explicit denyTrue"deny""denied by user""denied by user"

Regression coverage

Added TestGatewayApprovalCallbackUnavailable::test_unregister_notify_reports_callback_unavailable:

  1. Registers a gateway notify callback
  2. Starts check_all_command_guards() in a background thread
  3. Waits for the notify callback to fire (guard is now blocking)
  4. Calls unregister_gateway_notify() to simulate session teardown
  5. Asserts the message says "approval callback unavailable", not "denied by user"

Testing

  • 153 tests pass via scripts/run_tests.sh tests/tools/test_command_guards.py tests/tools/test_approval.py
  • No regressions in existing approval flow tests

Fixes Approval flow can return 'User denied' when no prompt is delivered #22992

Changed files

  • gateway/platforms/whatsapp.py (modified, +4/-1)
  • tests/tools/test_command_guards.py (modified, +61/-0)
  • tests/tools/test_file_tools.py (modified, +37/-0)
  • tools/approval.py (modified, +6/-1)
  • tools/file_tools.py (modified, +6/-6)
RAW_BUFFERClick to expand / collapse

Summary

GitHub write actions can be blocked with:

BLOCKED: User denied. Do NOT retry.

in sessions where no approval prompt is visibly delivered to the user.

Why this is a problem

The current message implies the user explicitly denied the action, but in this failure mode no interactive approval UI reaches the user. This is confusing and makes troubleshooting harder.

Observed behavior

  • Attempting external side-effect actions (e.g., posting GitHub issue comments) is blocked.
  • Tool returns: BLOCKED: User denied. Do NOT retry.
  • User reports they never received approve/deny prompt.

Suspected area

  • Approval routing/callback delivery path for the current session/UI surface.
  • Fail-closed path appears to map missing/unreachable approval callback to a "user denied" outcome/message.

Requested fix

  1. Differentiate explicit user denial from no-prompt/no-callback conditions.
  2. Return a clearer error for missing approval route (e.g., approval prompt not delivered / approval callback unavailable).
  3. Surface where approval was expected to appear (platform/session/thread) in diagnostic metadata/logs.

References

  • Denial text emitted in approval code path: tools/approval.py (BLOCKED: User denied. Do NOT retry.)
  • Related stale model-list issue where blocked comment attempt happened: #22990

Impact

  • Misattributes blocked side effects to user choice.
  • Causes repeated confusion and unnecessary auth/debug work.
  • Makes gateway/session approval issues harder to diagnose quickly.

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…

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 - ✅(Solved) Fix Approval flow can return 'User denied' when no prompt is delivered [1 pull requests, 1 participants]