Skip to content

fix: handle asyncio/selector failures in interactive prompt gracefully#1579

Merged
rrajan94 merged 2 commits into
Tracer-Cloud:mainfrom
Dhaxor:main
May 7, 2026
Merged

fix: handle asyncio/selector failures in interactive prompt gracefully#1579
rrajan94 merged 2 commits into
Tracer-Cloud:mainfrom
Dhaxor:main

Conversation

@Dhaxor

@Dhaxor Dhaxor commented May 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #1569

Describe the changes you have made in this PR -

app/cli/support/prompt_support.py: In _patched_ask(), added except (OSError, KeyError, RuntimeError) to catch asyncio/selector failures when prompt_toolkit registers stdin (fd 0) in the event loop. Prints a clear message and calls sys.exit(1) so the process exits cleanly instead of crashing with a cryptic traceback

Demo/Screenshot for feature changes and bug fixes -

Before (crash):

$ opensre onboard
? How do you want to get started?
KeyError: '0 is not registered'
  File "asyncio/selector_events.py", line 282, in _add_reader
    key = self._selector.get_key(fd)
  File "selectors.py", line 192, in get_key
    raise KeyError("{!r} is not registered".format(fileobj)) from None

OSError: [Errno 22] Invalid argument

After (clean exit):


$ opensre onboard
? How do you want to get started?

The interactive prompt could not be displayed due to a terminal I/O error on this platform.

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

  • What problem does your code solve? When prompt_toolkit's asyncio event loop tries to register stdin (fd 0) via epoll_ctl or select, the syscall can fail on some platforms, raising OSError or KeyError. These propagate unhandled because _patched_ask only catches KeyboardInterrupt, and main() only catches SystemExit/ClickException — so the user sees a full crash traceback.
  • What alternative approaches did you consider? I considered setting an asyncio event loop policy (e.g. SelectSelector fallback) but that's fragile and platform-specific. I also considered adding a general except Exception to main(), but that would suppress legitimate bugs elsewhere. The targeted catch at the failure site is the most minimal and safe.
  • Why did you choose this specific implementation? The _patched_ask closure is the exact point where the asyncio failure occurs. Catching (OSError, KeyError, RuntimeError) there and calling sys.exit(1) gives the user a clear message. SystemExit(1) propagates untouched through Exception handlers (it inherits from BaseException) and is cleanly caught by main()'s existing except SystemExit handler.
  • What are the key functions/components and what do they do? _patched_ask() is a closure that wraps questionary's unsafe_ask() in a retry loop for Ctrl+C double-exit handling. It's installed globally by install_questionary_ctrl_c_double_exit() onto every questionary prompt type (select, text, confirm, etc.). The new catch block covers the selector failure path without interfering with the existing Ctrl+C and Escape handling.

Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Greptile code review

This repo uses Greptile for automated review. Before merge, aim for Confidence Score: 5/5 with zero unresolved review threads — see CONTRIBUTING.md.

Run a review — add a PR comment with:

@greptile review

Give it ~30–60 seconds (sometimes longer) for results, then fix feedback and re-trigger until you reach Confidence Score: 5/5.

Optional: automate with the greploop skill.

@greptile-apps

greptile-apps Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a targeted exception handler to _patched_ask() in prompt_support.py, catching OSError and KeyError thrown by the asyncio/selector layer when it cannot register stdin on certain platforms. The fix logs the root cause at debug level and exits cleanly with a user-friendly message instead of crashing with a cryptic traceback.

  • (OSError, KeyError) is caught at the exact failure site inside the while True retry loop; sys.exit(1) raises SystemExit, which exits the loop and is caught by the existing handler in main().
  • RuntimeError (flagged in a previous review) is no longer included; the exception is bound to exc and emitted via logging.debug with exc_info=True so the full traceback is preserved for diagnostics.

Confidence Score: 5/5

Safe to merge — the change is a narrow, well-targeted exception handler at the exact failure site with no impact on the normal code path.

The handler catches only two specific exception types (OSError, KeyError) that correspond to confirmed crash cases, logs the full traceback at debug level, and exits via sys.exit(1) which propagates cleanly through the existing main() handler. The normal execution path is entirely unaffected.

No files require special attention.

Important Files Changed

Filename Overview
app/cli/support/prompt_support.py Adds targeted (OSError, KeyError) catch to _patched_ask() for asyncio/selector failures; logs at debug level and exits cleanly. Change is minimal and well-scoped.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["_patched_ask() called"] --> B["unsafe_ask()"]
    B -->|"clean result"| C["reset _last_ctrl_c\nreturn result"]
    B -->|"KeyboardInterrupt"| D{"_HardQuitInterrupt?"}
    D -->|"yes"| E["re-raise (Ctrl+Q hard quit)"]
    D -->|"no"| F{"second Ctrl+C\nwithin 2s?"}
    F -->|"yes"| G["print Goodbye\nsys.exit(0)"]
    F -->|"no"| H["print hint\nloop back"]
    B -->|"OSError / KeyError"| I["log at DEBUG\nwith exc_info"]
    I --> J["print platform I/O\nerror message"]
    J --> K["sys.exit(1) → SystemExit\ncaught by main()"]
Loading

Reviews (2): Last reviewed commit: "PR fixes" | Re-trigger Greptile

Comment thread app/cli/support/prompt_support.py Outdated
Comment thread app/cli/support/prompt_support.py Outdated
@cerencamkiran

Copy link
Copy Markdown
Collaborator

@greptileai review

@rrajan94 rrajan94 merged commit 4c9f3ef into Tracer-Cloud:main May 7, 2026
8 checks passed
@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

🛸 Aliens watching our repo just upgraded @Dhaxor's threat level to: do not engage — too competent. 👽


👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome.

kespineira added a commit to kespineira/opensre that referenced this pull request May 7, 2026
Pulls in Tracer-Cloud#1576 (E402/ARG001 noqa cleanup) and Tracer-Cloud#1579 (asyncio/selector
failure handling). Conflict resolved in app/main.py: kept upstream's
refactor that defers load_dotenv + module imports into main() to drop
the E402 noqas, and reapplied this branch's `init_sentry(entrypoint=
"cli")` on top.
Davidson3556 pushed a commit to Davidson3556/opensre that referenced this pull request May 15, 2026
Tracer-Cloud#1579)

* fix: handle asyncio/selector failures in interactive prompt gracefully

* PR fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OSError: [Errno 22] Invalid argument

3 participants