hermes - ✅(Solved) Fix [Bug]: `hermes gateway` prints KeyboardInterrupt traceback on Ctrl+C instead of exiting cleanly [4 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#17810Fetched 2026-05-01 05:55:41
View on GitHub
Comments
0
Participants
1
Timeline
9
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×4labeled ×4closed ×1

Error Message

┌─────────────────────────────────────────────────────────┐ │ ⚕ Hermes Gateway Starting... │ ├─────────────────────────────────────────────────────────┤ │ Messaging platforms + cron scheduler │ │ Press Ctrl+C to stop │ └─────────────────────────────────────────────────────────┘ Traceback (most recent call last): File ".../asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) File ".../asyncio/base_events.py", line 725, in run_until_complete return future.result() File ".../hermes-agent/gateway/run.py", line 12637, in start_gateway await runner.wait_for_shutdown() File ".../hermes-agent/gateway/run.py", line 3260, in wait_for_shutdown await self._shutdown_event.wait() File ".../asyncio/locks.py", line 213, in wait await fut asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "<frozen runpy>", line 198, in _run_module_as_main ... File ".../hermes_cli/gateway.py", line 2371, in run_gateway success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity)) File ".../asyncio/runners.py", line 195, in run return runner.run(main) File ".../asyncio/runners.py", line 123, in run raise KeyboardInterrupt() KeyboardInterrupt

Root Cause

Root Cause Analysis (optional)

Fix Action

Fixed

PR fix notes

PR #17813: fix(gateway): Clean shutdown while preserving SIGTERM failure semantics

Description (problem / solution / changelog)

Summary

  • Handle foreground gateway Ctrl+C without printing a chained CancelledError / KeyboardInterrupt traceback.
  • Install a signal.signal fallback when loop.add_signal_handler() is unavailable, so Windows-style Ctrl+C enters the normal gateway shutdown path.
  • Keep SIGTERM restart-on-failure semantics intact for systemd recovery, while treating SIGINT as a clean user stop.

Closes #17810

Validation

  • scripts/run_tests.sh tests/hermes_cli/test_gateway.py tests/gateway/test_runner_startup_failures.py -q -> 33 passed
  • git diff --check
  • Manual simulated Windows/no-add_signal_handler Ctrl+C path exits with Gateway stopped. and code 0

Changed files

  • gateway/run.py (modified, +53/-13)
  • hermes_cli/gateway.py (modified, +7/-2)
  • tests/gateway/test_runner_startup_failures.py (modified, +145/-0)
  • tests/hermes_cli/test_gateway.py (modified, +19/-1)

PR #17817: fix(cli): suppress KeyboardInterrupt traceback on Ctrl+C gateway stop (#17810)

Description (problem / solution / changelog)

Summary

Wraps the asyncio.run(start_gateway(...)) call in run_gateway() with a try/except KeyboardInterrupt that prints a clean exit message instead of a confusing chained traceback.

Problem

The startup banner says Press Ctrl+C to stop, but doing so printed:

asyncio.exceptions.CancelledError
...
KeyboardInterrupt

This makes a normal, expected user action look like a crash.

Fix

try:
    success = asyncio.run(start_gateway(...))
except KeyboardInterrupt:
    print('\n⚕ Gateway stopped.')
    sys.exit(0)

Clean, one-line exit. No traceback.

Closes #17810

Changed files

  • hermes_cli/gateway.py (modified, +5/-1)

PR #17854: fix: handle gateway Ctrl+C shutdown cleanly

Description (problem / solution / changelog)

Summary

  • catch KeyboardInterrupt around foreground gateway startup so hermes gateway exits cleanly on Ctrl+C
  • print a short shutdown message instead of surfacing the asyncio traceback
  • add regression coverage for both Ctrl+C shutdown and the existing nonzero failure path

Verification

  • scripts/run_tests.sh tests/hermes_cli/test_gateway.py -q
  • scripts/run_tests.sh tests/hermes_cli/test_gateway.py tests/hermes_cli/test_gateway_service.py -q
  • independent reviewer pass via delegation (no security or logic issues found)

Closes #17810

Followed CONTRIBUTING.md and AGENTS.md.

Changed files

  • hermes_cli/gateway.py (modified, +5/-1)
  • tests/hermes_cli/test_gateway.py (modified, +48/-1)

PR #17864: fix: catch KeyboardInterrupt in gateway for clean Ctrl+C exit

Description (problem / solution / changelog)

Summary

Wraps asyncio.run(start_gateway(...)) in try/except KeyboardInterrupt so that pressing Ctrl+C — the documented way to stop the gateway — exits with a clean message instead of printing a chained CancelledError / KeyboardInterrupt traceback.

Before

Press Ctrl+C to stop
Traceback (most recent call last):
  File ".../asyncio/runners.py", line 118, in run
    ...
asyncio.exceptions.CancelledError
...
KeyboardInterrupt

After

Press Ctrl+C to stop
^C
Gateway stopped.

Changes

  • hermes_cli/gateway.py: Wrap the asyncio.run() call in a try/except KeyboardInterrupt block that prints "\nGateway stopped." and sets success = True

This is platform-agnostic — the same exception is raised on Linux, macOS, and Windows.

Fixes #17810

Changed files

  • hermes_cli/gateway.py (modified, +5/-1)

Code Example

┌─────────────────────────────────────────────────────────┐
│           ⚕ Hermes Gateway Starting...├─────────────────────────────────────────────────────────┤
Messaging platforms + cron scheduler                    │
Press Ctrl+C to stop                                    │
└─────────────────────────────────────────────────────────┘
Traceback (most recent call last):
  File ".../asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
  File ".../asyncio/base_events.py", line 725, in run_until_complete
    return future.result()
  File ".../hermes-agent/gateway/run.py", line 12637, in start_gateway
    await runner.wait_for_shutdown()
  File ".../hermes-agent/gateway/run.py", line 3260, in wait_for_shutdown
    await self._shutdown_event.wait()
  File ".../asyncio/locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  ...
  File ".../hermes_cli/gateway.py", line 2371, in run_gateway
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
  File ".../asyncio/runners.py", line 195, in run
    return runner.run(main)
  File ".../asyncio/runners.py", line 123, in run
    raise KeyboardInterrupt()
KeyboardInterrupt

---

C:\Users\HUAWEI>hermes gateway
┌─────────────────────────────────────────────────────────┐
│           ⚕ Hermes Gateway Starting...├─────────────────────────────────────────────────────────┤
Messaging platforms + cron scheduler                    │
Press Ctrl+C to stop                                   │
└─────────────────────────────────────────────────────────┘

Traceback (most recent call last):
  File "D:\Software\Anaconda3\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "D:\Software\Anaconda3\Lib\asyncio\base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\gateway\run.py", line 12637, in start_gateway
    await runner.wait_for_shutdown()
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\gateway\run.py", line 3260, in wait_for_shutdown
    await self._shutdown_event.wait()
  File "D:\Software\Anaconda3\Lib\asyncio\locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\venv\Scripts\hermes.exe\__main__.py", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\main.py", line 10103, in main
    args.func(args)
    ~~~~~~~~~^^^^^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\main.py", line 1345, in cmd_gateway
    gateway_command(args)
    ~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\gateway.py", line 4038, in gateway_command
    return _gateway_command_inner(args)
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\gateway.py", line 4056, in _gateway_command_inner
    run_gateway(verbose, quiet=quiet, replace=replace)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\gateway.py", line 2371, in run_gateway
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
  File "D:\Software\Anaconda3\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "D:\Software\Anaconda3\Lib\asyncio\runners.py", line 123, in run
    raise KeyboardInterrupt()
KeyboardInterrupt
^C

---



---

try:
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
except KeyboardInterrupt:
    print("\nGateway stopped.")
    success = True
RAW_BUFFERClick to expand / collapse

Bug Description

When stopping the gateway with Ctrl+C — the documented way to stop it (the startup banner literally says Press Ctrl+C to stop) — the process prints a chained CancelledError / KeyboardInterrupt traceback before exiting. A normal, expected user action looks like a crash.

Steps to Reproduce

  1. Run hermes gateway
  2. Wait for the startup banner to appear
  3. Press Ctrl+C to stop

Expected Behavior

The gateway exits cleanly, ideally with a short message like Gateway stopped. and no traceback.

Actual Behavior

A confusing traceback is printed:

┌─────────────────────────────────────────────────────────┐
│           ⚕ Hermes Gateway Starting...                 │
├─────────────────────────────────────────────────────────┤
│  Messaging platforms + cron scheduler                    │
│  Press Ctrl+C to stop                                    │
└─────────────────────────────────────────────────────────┘
Traceback (most recent call last):
  File ".../asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
  File ".../asyncio/base_events.py", line 725, in run_until_complete
    return future.result()
  File ".../hermes-agent/gateway/run.py", line 12637, in start_gateway
    await runner.wait_for_shutdown()
  File ".../hermes-agent/gateway/run.py", line 3260, in wait_for_shutdown
    await self._shutdown_event.wait()
  File ".../asyncio/locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  ...
  File ".../hermes_cli/gateway.py", line 2371, in run_gateway
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
  File ".../asyncio/runners.py", line 195, in run
    return runner.run(main)
  File ".../asyncio/runners.py", line 123, in run
    raise KeyboardInterrupt()
KeyboardInterrupt

Affected Component

Gateway (Telegram/Discord/Slack/WhatsApp)

Messaging Platform (if gateway-related)

No response

Debug Report

C:\Users\HUAWEI>hermes gateway
┌─────────────────────────────────────────────────────────┐
│           ⚕ Hermes Gateway Starting...                 │
├─────────────────────────────────────────────────────────┤
│  Messaging platforms + cron scheduler                    │
│  Press Ctrl+C to stop                                   │
└─────────────────────────────────────────────────────────┘

Traceback (most recent call last):
  File "D:\Software\Anaconda3\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "D:\Software\Anaconda3\Lib\asyncio\base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\gateway\run.py", line 12637, in start_gateway
    await runner.wait_for_shutdown()
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\gateway\run.py", line 3260, in wait_for_shutdown
    await self._shutdown_event.wait()
  File "D:\Software\Anaconda3\Lib\asyncio\locks.py", line 213, in wait
    await fut
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\venv\Scripts\hermes.exe\__main__.py", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\main.py", line 10103, in main
    args.func(args)
    ~~~~~~~~~^^^^^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\main.py", line 1345, in cmd_gateway
    gateway_command(args)
    ~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\gateway.py", line 4038, in gateway_command
    return _gateway_command_inner(args)
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\gateway.py", line 4056, in _gateway_command_inner
    run_gateway(verbose, quiet=quiet, replace=replace)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\HUAWEI\AppData\Local\hermes\hermes-agent\hermes_cli\gateway.py", line 2371, in run_gateway
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
  File "D:\Software\Anaconda3\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "D:\Software\Anaconda3\Lib\asyncio\runners.py", line 123, in run
    raise KeyboardInterrupt()
KeyboardInterrupt
^C

Operating System

Windows 11

Python Version

3.13.5

Hermes Version

Hermes Agent v0.11.0 (2026.4.23)

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

In hermes_cli/gateway.py, run_gateway() calls asyncio.run(start_gateway(...)) without catching KeyboardInterrupt. When the user presses Ctrl+C, asyncio cancels the awaited _shutdown_event.wait() and re-raises KeyboardInterrupt, which propagates all the way out of the CLI entry point.

This is platform-agnostic behavior of asyncio.run() — the same exception will be raised on Linux and macOS. It just happens to be most visible on Windows because the shell doesn't suppress stderr by default.

Proposed Fix (optional)

Wrap the call in hermes_cli/gateway.py (around line 2371):

try:
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
except KeyboardInterrupt:
    print("\nGateway stopped.")
    success = True

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

extent analysis

TL;DR

The proposed fix involves wrapping the asyncio.run() call in a try-except block to catch the KeyboardInterrupt exception and print a clean shutdown message.

Guidance

  • Verify that the issue is indeed caused by the uncaught KeyboardInterrupt exception in hermes_cli/gateway.py.
  • Apply the proposed fix by wrapping the asyncio.run() call in a try-except block to catch KeyboardInterrupt and print a clean shutdown message.
  • Test the fix by running the gateway and pressing Ctrl+C to stop it, ensuring that it exits cleanly with the expected message.
  • Consider submitting a PR with the proposed fix to resolve the issue.

Example

The proposed fix can be applied as follows:

try:
    success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))
except KeyboardInterrupt:
    print("\nGateway stopped.")
    success = True

Notes

This fix assumes that the issue is indeed caused by the uncaught KeyboardInterrupt exception and that the proposed fix is correct. Further testing and verification may be necessary to ensure that the fix resolves the issue.

Recommendation

Apply the proposed workaround by wrapping the asyncio.run() call in a try-except block to catch KeyboardInterrupt and print a clean shutdown message, as this is a straightforward and effective solution to the issue.

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 [Bug]: `hermes gateway` prints KeyboardInterrupt traceback on Ctrl+C instead of exiting cleanly [4 pull requests, 1 participants]