Skip to content

fix: guard interactive startup without tty - #19092

Open
TinyTb wants to merge 1 commit into
NousResearch:mainfrom
TinyTb:fix/setup-tty-shell-guidance
Open

fix: guard interactive startup without tty#19092
TinyTb wants to merge 1 commit into
NousResearch:mainfrom
TinyTb:fix/setup-tty-shell-guidance

Conversation

@TinyTb

@TinyTb TinyTb commented May 3, 2026

Copy link
Copy Markdown

Problem

During initial setup, Hermes could offer to start the setup/chat flow even when it did not have a usable interactive TTY. In that state, the prompt_toolkit CLI could print the welcome/goodbye text and then crash with a traceback like:

KeyError: '0 is not registered'
...
OSError: [Errno 22] Invalid argument

This happens because the interactive CLI tries to attach an asyncio reader to stdin fd 0, but fd 0 is not a selectable terminal in non-interactive contexts. The resulting traceback is confusing for new users and makes setup look broken.

There was also a README post-install example that led with source ~/.bashrc, even though macOS users on the default zsh shell need source ~/.zshrc.

Summary

  • fail early with a clear error when interactive hermes/hermes chat is launched without a TTY, while preserving hermes chat -q for non-interactive use
  • skip the legacy setup script's "run setup wizard now" prompt when stdin is not interactive, instead telling users to run hermes setup later
  • update README post-install instructions to show zsh/macOS and bash shell reload commands separately

Test Plan

  • /Users/hermes/.hermes/hermes-agent/venv/bin/python -m pytest tests/hermes_cli/test_setup_noninteractive.py tests/hermes_cli/test_setup_hermes_script.py -q
  • /Users/hermes/.hermes/hermes-agent/venv/bin/python -m py_compile hermes_cli/main.py hermes_cli/setup.py
  • bash -n setup-hermes.sh

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard labels May 3, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #8641 (root issue) — competing with open PRs #8420 and #8842 which implement the same TTY guard.

1 similar comment
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #8641 (root issue) — competing with open PRs #8420 and #8842 which implement the same TTY guard.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the no-TTY startup path. A configured classic chat still reaches cli.main() without an early TTY gate on current head (hermes_cli/main.py:2404-2430), while the legacy developer setup script still prompts unconditionally (setup-hermes.sh:455-461).

Problems

  • The proposed guard checks only args.query (hermes_cli/main.py:1270 in this PR). That would reject --image with no TTY, although current main treats an image as a non-interactive single-query input (hermes_cli/_parser.py:272; hermes_cli/main.py:12560).
  • The first-run setup portion is already covered: current main exits with non-interactive guidance before prompting (hermes_cli/main.py:2327-2331), and commit 256bedb632ece7b9142a20f4e830f5a5fe48ad5f removed post-setup chat relaunching.

Suggested changes

  • Salvage the direct guard against current cmd_chat(), exempting both query and image single-query modes, and add a no-TTY image regression test.
  • Keep the setup-script and README changes scoped to the legacy contributor path.

Automated hermes-sweeper review.

Comment thread hermes_cli/main.py
# UI will crash on some platforms (notably macOS/kqueue) if stdin is not a
# selectable TTY. Fail early with actionable guidance instead of showing a
# traceback from loop.add_reader(fd=0).
if not getattr(args, "query", None) and not sys.stdin.isatty():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--image is also a single-query input: current dispatch uses not query and not image to identify an interactive prompt (hermes_cli/main.py:12560). Please exempt args.image here as well and add a no-TTY image-path regression test; otherwise this guard rejects a supported headless single-query mode.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants