Skip to content

prompt_toolkit crashes with NoConsoleScreenBufferError on Windows when hermes chat -q stdout is piped (non-interactive/automated use) #65558

Description

@bradleyfirkus-pixel

Summary

On Windows, hermes chat -q "<prompt>" crashes with prompt_toolkit.output.win32.NoConsoleScreenBufferError: No Windows console found. Are you running cmd.exe? whenever Hermes' stdout is not a real console — e.g. when it is spawned by another process with stdio: pipe (any automation/orchestration tool, CI, or programmatic integration). This makes the documented "single query (non-interactive mode)" flag unusable from any Windows automation context, since capturing the response text inherently requires piping stdout.

Environment

  • Hermes Agent v0.10.0 (2026.4.16, upstream 4a69a662)
  • Windows (child process spawned via Node.js child_process.spawn with stdio: ['ignore','pipe','pipe'], shell: false)
  • prompt_toolkit output backend selection

Steps to reproduce

Spawn Hermes as a non-console child process (this is exactly what any orchestrator/automation wrapper does to capture output):

const { spawn } = require('child_process');
const child = spawn('hermes', ['chat', '-q', 'Reply with exactly the single word: PONG', '-Q'], {
  stdio: ['ignore', 'pipe', 'pipe'],
  shell: false,
});

or equivalently from a shell with stdout redirected to a pipe/file rather than a terminal:

hermes chat -q "Reply with exactly the single word: PONG" -Q | Out-File out.txt

Actual result

Exit code 1, no query response, and (without -Q, or in some -Q cases too) a traceback ending in:

Traceback (most recent call last):
  ...
  File "...\hermes-agent\cli.py", line 8321, in chat
    _cprint(f"{_DIM}Initializing agent...{_RST}")
  File "...\hermes-agent\cli.py", line 1254, in _cprint
    _pt_print(_PT_ANSI(text))
  File "...\prompt_toolkit\shortcuts\utils.py", line 111, in print_formatted_text
    output = get_app_session().output
  File "...\prompt_toolkit\application\current.py", line 67, in output
    self._output = create_output()
  File "...\prompt_toolkit\output\defaults.py", line 91, in create_output
    return Win32Output(stdout, default_color_depth=color_depth_from_env)
  File "...\prompt_toolkit\output\win32.py", line 115, in __init__
    info = self.get_win32_screen_buffer_info()
  File "...\prompt_toolkit\output\win32.py", line 219, in get_win32_screen_buffer_info
    raise NoConsoleScreenBufferError
prompt_toolkit.output.win32.NoConsoleScreenBufferError: No Windows console found. Are you running cmd.exe?

Separately, without forcing PYTHONIOENCODING=utf-8 in the child environment, the banner/spinner rendering (box-drawing + braille characters) also raises UnicodeEncodeError: 'charmap' codec can't encode characters ... when stdout falls back to the default Windows console codepage (cp1252) instead of UTF-8 — a second, related issue with the same root cause (assuming a real interactive console is always present).

Root cause

prompt_toolkit/output/defaults.py::create_output() has this branch on sys.platform == "win32":

if sys.platform == "win32":
    if is_win_vt100_enabled():
        return Windows10_Output(...)
    if is_conemu_ansi():
        return ConEmuOutput(...)
    else:
        return Win32Output(stdout, default_color_depth=color_depth_from_env)

Unlike the POSIX branch just below it (which checks stdout.isatty() and falls back to PlainTextOutput when stdout is not a terminal), the Windows branch has no such fallback — it unconditionally tries Win32Output, which calls GetConsoleScreenBufferInfo on the process's STD_OUTPUT_HANDLE. When that handle is a pipe (not a console), the Win32 API call fails and Win32Output.__init__ raises NoConsoleScreenBufferError with no graceful degradation path.

This means any non-interactive/automated invocation of Hermes on Windows is currently broken, since capturing output for programmatic use always requires stdio: pipe.

Suggested fix

Either (or both):

  1. In Hermes' own cli.py _cprint, catch prompt_toolkit.output.win32.NoConsoleScreenBufferError (and ideally check sys.stdout.isatty() up front) and fall back to plain print() when no real console is attached — mirrors what prompt_toolkit already does correctly on POSIX.
  2. Upstream in prompt_toolkit itself: add an isatty()/pipe check to the win32 branch of create_output() (parity with the POSIX branch), returning PlainTextOutput when stdout isn't a real console, instead of unconditionally requiring Win32Output's console-buffer query to succeed.

Workaround

Set PYTHONIOENCODING=utf-8 in the child environment to avoid the separate charmap crash on the banner text, and use -Q (quiet mode) to suppress most _cprint calls — but this does not fully avoid the NoConsoleScreenBufferError path for every non-interactive invocation on Windows, since some _cprint calls still fire under -q/-Q (e.g. the "Initializing agent..." message) whenever there's no attached console at all.

Happy to test a fix against a real automation harness if useful — this was found while wiring Hermes up as a pluggable local LLM backend in an orchestration tool and confirmed via a minimal repro spawning hermes.exe directly with stdio: pipe, isolating it from any surrounding tooling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/cliCLI entry point, hermes_cli/, setup wizardneeds-reproBug needs reproduction stepsplatform/windowsNative Windows-specific behavior or breakagesweeper:implemented-on-mainSweeper: behavior already present on current mainsweeper:risk-platform-windowsSweeper risk: may break or behave differently on native Windowstype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions