hermes - ✅(Solved) Fix fix(cli): RuntimeWarning in /new, /clear, /undo, /reload-mcp — coroutine was never awaited [6 pull requests, 2 comments, 3 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#22970Fetched 2026-05-11 03:31:59
View on GitHub
Comments
2
Participants
3
Timeline
16
Reactions
0
Timeline (top)
cross-referenced ×9labeled ×3commented ×2referenced ×2

Root Cause

_prompt_text_input() in cli.py calls run_in_terminal(_ask) without checking whether it's on the main thread. The sibling method _run_curses_picker() already has a threading.current_thread() is threading.main_thread() guard and falls back to a direct call when on a background thread.

When slash commands are dispatched from process_loop (which runs in a background thread), run_in_terminal returns a coroutine that can't be scheduled on the main event loop, producing the warning.

Fix Action

Fix / Workaround

When slash commands are dispatched from process_loop (which runs in a background thread), run_in_terminal returns a coroutine that can't be scheduled on the main event loop, producing the warning.

PR fix notes

PR #22972: fix(cli): prevent RuntimeWarning in _prompt_text_input from background thread

Description (problem / solution / changelog)

Summary

_prompt_text_input() called run_in_terminal() without checking whether it was on the main thread. When /new, /clear, /undo, or /reload-mcp were dispatched from process_loop (background thread), run_in_terminal returned a coroutine that was never awaited, producing:

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

Root Cause

_prompt_text_input() (line 5870) checked if self._app: (does a prompt_toolkit app exist?) but never checked threading.current_thread() is threading.main_thread() (am I on the main event loop thread?). run_in_terminal requires the asyncio event loop, which only exists on the main thread.

The sibling method _run_curses_picker() (line 5830) already has this guard — it was added when curses integration was implemented — but _prompt_text_input was never updated to match.

Fix

Add a threading.main_thread() check before calling run_in_terminal, falling back to a blocking input() call when not on the main thread — identical to the pattern in _run_curses_picker().

Affected Commands

  • /new (and /reset)
  • /clear
  • /undo
  • /reload-mcp

How to Test

  1. Start a multi-turn agent run (e.g. message that triggers tool calls)
  2. While spinner is active, type /new
  3. Confirm prompt should appear without RuntimeWarning
  4. Repeat for /clear, /undo, /reload-mcp

Platform Tested

  • Linux (WSL2 on Windows 11)

Closes #22970

Changed files

  • cli.py (modified, +8/-1)

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 #23035: fix(cli): guard run_in_terminal against background thread RuntimeWarning

Description (problem / solution / changelog)

Closing as duplicate. Duplicate of #22972 — same main-thread guard approach for run_in_terminal in _prompt_text_input().

Should have checked is:pr is:open before submitting — lesson learned.

Changed files

  • cli.py (modified, +40/-8)

PR #23078: fix(cli): add main-thread guard to _prompt_text_input — prevent RuntimeWarning (#22970)

Description (problem / solution / changelog)

Fixes #22970.

When /new, /clear, /undo, or /reload-mcp are triggered mid-turn (from a background thread), _prompt_text_input called run_in_terminal without checking the thread. run_in_terminal returns a coroutine that can't be scheduled, causing:

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

Fix mirrors the guard already present in _run_curses_picker:

in_main_thread = threading.current_thread() is threading.main_thread()
if self._app and in_main_thread:
    run_in_terminal(_ask)
else:
    _ask()  # direct call — safe from any thread

2 tests.

Changed files

  • cli.py (modified, +7/-1)
  • tests/cli/test_cli_prompt_thread_guard.py (added, +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)

PR #23493: fix(cli): schedule destructive slash prompts on app loop

Description (problem / solution / changelog)

Summary

  • schedule destructive slash confirmation prompts on the prompt_toolkit application loop when called from process_loop
  • wait for the terminal prompt to complete before continuing command processing
  • add regression coverage for /clear confirmation from a worker thread

Test Plan

  • scripts/run_tests.sh tests/cli/test_destructive_slash_confirm.py tests/cli/test_cli_new_session.py -q
  • python -m py_compile cli.py tests/cli/test_destructive_slash_confirm.py tests/cli/test_cli_new_session.py
  • scripts/run_tests.sh tests/cli -q (if run)

Changed files

  • cli.py (modified, +80/-17)
  • tests/cli/test_destructive_slash_confirm.py (modified, +74/-0)

Code Example

RuntimeWarning: coroutine 'run_in_terminal.<locals>.run' was never awaited
RAW_BUFFERClick to expand / collapse

Bug Description

Running /new, /clear, /undo, or /reload-mcp mid-turn produces a RuntimeWarning:

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

The command still works (the confirmation prompt appears and input is accepted), but the warning is logged and the stack trace clutters output.

Steps to Reproduce

  1. Send a message that triggers a multi-turn agent run (e.g. "run a long build")
  2. While the spinner is active, type /new
  3. Observe the confirmation prompt
  4. After selecting a choice, the RuntimeWarning appears in logs

Root Cause

_prompt_text_input() in cli.py calls run_in_terminal(_ask) without checking whether it's on the main thread. The sibling method _run_curses_picker() already has a threading.current_thread() is threading.main_thread() guard and falls back to a direct call when on a background thread.

When slash commands are dispatched from process_loop (which runs in a background thread), run_in_terminal returns a coroutine that can't be scheduled on the main event loop, producing the warning.

Affected Commands

All destructive slash commands that use _confirm_destructive_slash()_prompt_text_input():

  • /new (and /reset)
  • /clear
  • /undo

Also /reload-mcp which uses _confirm_and_reload_mcp()_prompt_text_input().

Environment

  • Hermes version: main branch (May 2026)
  • OS: Linux (WSL2 on Windows 11)
  • Python: 3.11+

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 fix(cli): RuntimeWarning in /new, /clear, /undo, /reload-mcp — coroutine was never awaited [6 pull requests, 2 comments, 3 participants]