Skip to content

fix(cli): also suppress OSError EINVAL from kqueue stdin register (#6393)#13251

Closed
tumbleweedlabs wants to merge 1 commit into
NousResearch:mainfrom
tumbleweedlabs:fix/cli-stdin-einval-guard
Closed

fix(cli): also suppress OSError EINVAL from kqueue stdin register (#6393)#13251
tumbleweedlabs wants to merge 1 commit into
NousResearch:mainfrom
tumbleweedlabs:fix/cli-stdin-einval-guard

Conversation

@tumbleweedlabs
Copy link
Copy Markdown

Summary

Closes the EINVAL variant of the prompt_toolkit stdin-register crash filed as #6393. The existing guard already caught KeyError: "0 is not registered" and OSError: Bad file descriptor, but it was a substring match — so OSError: [Errno 22] Invalid argument slipped through and the user still got a raw traceback.

What changed

cli.py, in HermesCLI.run():

  1. Asyncio loop exception handler (_suppress_closed_loop_errors) — now also suppresses OSError whose errno is EBADF or EINVAL.
  2. Outer except (KeyError, OSError) around app.run() — detects unusable stdin by errno (EBADF / EINVAL) in addition to the existing substring checks.
  3. User-facing message — broadened to mention terminal-emulator PTY compatibility and suggest Terminal.app / iTerm2 as a workaround, since this manifests in several non-Apple terminal emulators on macOS 26, not just with uv-managed cPython.

Why

Reported by a user hitting the exact traceback in #6393 on macOS 26.4.1:

File ".../prompt_toolkit/input/vt100.py", line 165, in _attached_input
    loop.add_reader(fd, callback_wrapper)
  ...
  File ".../selectors.py", line 523, in register
    self._selector.control([kev], 0, 0)
OSError: [Errno 22] Invalid argument

Verified against that user's environment:

  • os.fstat(0) succeeds (so the existing fstat() guard passes)
  • os.isatty(0) returns True
  • kqueue's control([kev], 0, 0) with EVFILT_READ on fd 0 returns EINVAL — the fd is a char device but not one kqueue will watch for read.

The error string is "[Errno 22] Invalid argument", which doesn't contain "is not registered" or "Bad file descriptor", so the existing handler re-raised.

Why errno instead of broader string match

errno comparisons are robust across Python versions and localized strerror output. I kept the existing string checks as a belt-and-suspenders for cases where errno is missing (e.g. the KeyError path, or a re-wrapped exception).

How to test

Can't easily script the EINVAL case since it depends on the terminal emulator's PTY behavior, but the code path is easy to verify by injecting a synthetic exception:

import asyncio, errno
# ... in place of app.run():
raise OSError(errno.EINVAL, "Invalid argument")

With this change, that now prints the friendly diagnostic instead of re-raising.

Also verified python3 -m py_compile cli.py passes, and that hermes still launches cleanly in a working Terminal.app window.

Tested on

  • macOS 26.4.1 (Darwin 25.4.0) / cPython 3.11.15 (uv-managed)

Related

🤖 Generated with Claude Code

…usResearch#6393)

The existing NousResearch#6393 guard only caught the `KeyError: "0 is not registered"`
and `OSError: Bad file descriptor` flavors of the prompt_toolkit startup
crash. When launched from a terminal whose PTY is a char device that
macOS kqueue refuses to register for EVFILT_READ (observed with some
non-Apple terminal emulators on macOS 26), prompt_toolkit's
`add_reader(0, ...)` raises `OSError: [Errno 22] Invalid argument` and
the except block's string match ("is not registered" / "Bad file
descriptor") did not hit — so the user still saw a raw traceback.

Detect unusable stdin by errno (EBADF / EINVAL) in addition to the
substring checks, both in the asyncio loop exception handler and in the
outer `except (KeyError, OSError)` around `app.run()`. Broaden the
user-facing message to mention terminal-emulator compatibility and
suggest Terminal.app / iTerm2 as a workaround.

Reproduced original crash, verified new guard produces the friendly
message instead of a traceback on macOS 26.4.1 / cPython 3.11.15.
@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 Apr 22, 2026
@alt-glitch
Copy link
Copy Markdown
Collaborator

Related to #11253, #11866, #8796, #9996 — all attempt to fix the same kqueue stdin EINVAL crash (#6393). This PR should be evaluated against those for best approach.

@teknium1
Copy link
Copy Markdown
Contributor

Closing — superseded by #26077 (merged as commit d3d5916), which preventively probes kqueue at startup and falls back to SelectSelector when fd 0 cannot be registered. The widened except-clause matching EINVAL / EBADF / 'Invalid argument' — which most PRs in this cluster including yours added — is also included.

Thanks for the fix; closing as duplicate of the merged work.

@teknium1 teknium1 closed this May 15, 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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: after install it, startup failed

3 participants