hermes - ✅(Solved) Fix bug: /reload-mcp triggers RuntimeWarning from unawaited run_in_terminal coroutine [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

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#23009Fetched 2026-05-11 03:31:44
View on GitHub
Comments
1
Participants
2
Timeline
7
Reactions
0
Timeline (top)
labeled ×3cross-referenced ×2closed ×1commented ×1

Running /reload-mcp triggers a RuntimeWarning: coroutine "run_in_terminal.<locals>.run" was never awaited that gets caught in the process_loop exception handler and logged at cli.py:12433.

Error Message

Running /reload-mcp triggers a RuntimeWarning: coroutine "run_in_terminal.<locals>.run" was never awaited that gets caught in the process_loop exception handler and logged at cli.py:12433.

Root Cause

The bug is in _prompt_text_input() at cli.py:5876. When called from a background thread (the process_loop thread), it invokes prompt_toolkit.application.run_in_terminal() which returns a Future (via ensure_future(run())). Since _prompt_text_input is a synchronous method that does not await this Future, Python raises RuntimeWarning when the unretrieved Future/coroutine is garbage collected.

Fix Action

Fix

The sibling method _curses_single_select() (line ~5843) already has the correct pattern:

in_main_thread = threading.current_thread() is threading.main_thread()
if self._app and in_main_thread:
    run_in_terminal(...)
else:
    ...  # direct call

The fix adds the same in_main_thread guard to _prompt_text_input(). When run_in_terminal would be called from a background thread, it falls through to the synchronous input() path instead.

PR fix notes

PR #22987: fix(cli): schedule prompt_toolkit terminal awaitables

Description (problem / solution / changelog)

Summary

  • Schedule prompt_toolkit.application.run_in_terminal(...) on the active prompt_toolkit app loop before calling it from background slash-command threads.
  • Fix destructive slash command confirmation prompts such as /new, /reset, /clear, and /undo when prompt_toolkit returns an awaitable.
  • Apply the same awaitable handling to related terminal-bridged call sites for curses picker, background _cprint, and Ctrl+Z suspend.
  • Add a regression test covering _prompt_text_input from a process-loop-like background thread while run_in_terminal executes on the app-loop thread.

Related issues / PRs

  • Related to #22958: destructive slash-command confirmation prompts in TUI mode.
  • Addresses the run_in_terminal(...) awaitable root cause of #22970 and duplicate report #23009: RuntimeWarning: coroutine 'run_in_terminal.<locals>.run' was never awaited.
  • Overlaps with #22989, which changes the prompt implementation itself (input() -> prompt_toolkit prompt). This PR focuses on safely scheduling/awaiting run_in_terminal(...) from background-thread call sites and includes a regression test for that path.

Why

/new and other destructive slash commands run their confirmation flow from the CLI process_loop background thread while prompt_toolkit owns the terminal on its application event loop. In prompt_toolkit 3.x, run_in_terminal(...) returns/schedules an awaitable and must be called from the app loop. Calling it directly from the background thread can drop the prompt and emit:

RuntimeWarning: coroutine 'run_in_terminal.<locals>.run' was never awaited

How to test

Reproduction before this fix:

  1. Start interactive Hermes CLI.
  2. Enter /new.
  3. Observe the destructive-command confirmation fail with the unawaited coroutine warning.

After this fix:

  1. Start interactive Hermes CLI.
  2. Enter /new.
  3. The confirmation prompt appears.
  4. Enter 3 to cancel.
  5. Hermes reports /new cancelled without the coroutine warning.

Platforms tested

  • macOS
  • Python 3.11
  • prompt_toolkit 3.0.52

Test Plan

Verified in a fresh clone following the CONTRIBUTING setup instructions on commit 3bc8df818.

Passed:

  • venv/bin/python -m py_compile cli.py tests/cli/test_cprint_bg_thread.py
  • venv/bin/python scripts/check-windows-footguns.py cli.py tests/cli/test_cprint_bg_thread.py
  • scripts/run_tests.sh tests/cli/test_cprint_bg_thread.py tests/hermes_cli/test_destructive_slash_confirm_gate.py
    • 17 passed

Manual verification:

  • Started Hermes with an isolated temporary home directory, ran /new, selected 3 to cancel, and verified the confirmation prompt worked without the unawaited coroutine warning.

Full-suite note:

  • Attempted scripts/run_tests.sh tests in a fresh clone. It did not complete green (16333 passed, 75 skipped, 33 failed, 5057 errors), with broad unrelated-looking failures across plugin/skill/TUI/website tests. A representative reported failing test passed when rerun in isolation, so I did not treat this as related to this CLI fix.
  • scripts/run_tests.sh with no args currently fails locally with ARGS[@]: unbound variable; scripts/run_tests.sh tests was used for the full-suite attempt.

Changed files

  • cli.py (modified, +80/-13)
  • tests/cli/test_cprint_bg_thread.py (modified, +72/-0)

PR #23257: fix(cli): avoid run_in_terminal from worker threads

Description (problem / solution / changelog)

What does this PR do?

This fixes #23009 with a narrow bugfix that keeps the affected path aligned with the current Hermes behavior without widening the change surface.

Related Issue

Fixes #23009

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • use direct input() when _prompt_text_input() is called off the main thread instead of forcing run_in_terminal()
  • add main-thread and worker-thread regression tests for the destructive slash confirm prompt path
  • Files: cli.py, tests/cli/test_destructive_slash_confirm.py

How to Test

  1. Run uv run --frozen pytest -q -o addopts='' tests/cli/test_destructive_slash_confirm.py -k 'prompt_text_input or gate_'
  2. Run uv run --frozen ruff check cli.py tests/cli/test_destructive_slash_confirm.py
  3. Confirm the affected workflow in #23009 now follows the expected behavior.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

<!-- Not applicable. -->

Screenshots / Logs

Prompt-thread regression tests and Ruff checks passed locally on macOS.

Changed files

  • cli.py (modified, +3/-1)
  • tests/cli/test_destructive_slash_confirm.py (modified, +42/-0)

Code Example

in_main_thread = threading.current_thread() is threading.main_thread()
if self._app and in_main_thread:
    run_in_terminal(...)
else:
    ...  # direct call

---

-        if self._app:
+        in_main_thread = threading.current_thread() is threading.main_thread()
+
+        if self._app and in_main_thread:
             from prompt_toolkit.application import run_in_terminal
RAW_BUFFERClick to expand / collapse

Description

Running /reload-mcp triggers a RuntimeWarning: coroutine "run_in_terminal.<locals>.run" was never awaited that gets caught in the process_loop exception handler and logged at cli.py:12433.

Root Cause

The bug is in _prompt_text_input() at cli.py:5876. When called from a background thread (the process_loop thread), it invokes prompt_toolkit.application.run_in_terminal() which returns a Future (via ensure_future(run())). Since _prompt_text_input is a synchronous method that does not await this Future, Python raises RuntimeWarning when the unretrieved Future/coroutine is garbage collected.

Steps to Reproduce

  1. Run Hermes CLI
  2. Type /reload-mcp
  3. Observe RuntimeWarning after responding to the confirmation prompt

Expected Behavior

No warnings. Confirmation prompts should work cleanly from any thread.

Fix

The sibling method _curses_single_select() (line ~5843) already has the correct pattern:

in_main_thread = threading.current_thread() is threading.main_thread()
if self._app and in_main_thread:
    run_in_terminal(...)
else:
    ...  # direct call

The fix adds the same in_main_thread guard to _prompt_text_input(). When run_in_terminal would be called from a background thread, it falls through to the synchronous input() path instead.

Patch

Applied locally. Key change in _prompt_text_input():

-        if self._app:
+        in_main_thread = threading.current_thread() is threading.main_thread()
+
+        if self._app and in_main_thread:
             from prompt_toolkit.application import run_in_terminal

Environment

  • prompt_toolkit 3.0.52
  • Hermes Agent latest (git main)

/label bug

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