Skip to content

fix(console): fail fast in select_with_arrows when stdin is not a TTY - #4155

Open
yunaremaia wants to merge 1 commit into
github:mainfrom
yunaremaia:fix/noninteractive-select-hang-4152
Open

fix(console): fail fast in select_with_arrows when stdin is not a TTY#4155
yunaremaia wants to merge 1 commit into
github:mainfrom
yunaremaia:fix/noninteractive-select-hang-4152

Conversation

@yunaremaia

Copy link
Copy Markdown

Fixes #4152

Problem

specify init can hang indefinitely at arrow-key selection prompts when stdin is not attached to a TTY (agent harness, CI, piped input). select_with_arrows blocks in readkey() waiting for keypresses that never arrive — no timeout, no error, no output. This matters because agent-driven workflows are precisely the environments with no TTY, and Spec Kit is a natural fit for them.

Fix (defense in depth)

select_with_arrows now detects non-interactive stdin (sys.stdin.isatty()) and:

  1. Resolves to default_key immediately when one is provided — making a fully scripted init expressible without needing a flag for every prompt (issue option 2).
  2. Raises ValueError naming the missing interactive session when no default exists — fail fast with an actionable message instead of hanging (issue option 1).

Both call sites in init.py already guard with _stdin_is_interactive(), so this change cannot alter existing behavior for guarded paths — it closes the hang for any future or unguarded caller of the selector (issue: "there is no flag that reaches every prompt").

Tests

New file tests/test_console_non_interactive.py (3 tests):

  • no-TTY without default → raises ValueError ("not a terminal")
  • no-TTY with default → resolves to the default (no hang)
  • TTY path → interactive loop untouched (get_key mocked to Enter)

Verified: 8/8 console tests green. tests/integrations/test_cli.py failures are pre-existing in this environment (84 failures with and without this change — identical count), so no regressions introduced.

…github#4152)

specify init can hang indefinitely at arrow-key selection prompts
(select_with_arrows) when stdin is not attached to a TTY — agent
harnesses, CI, or piped input wait on readkey() forever with no
timeout, no error, and no output.

select_with_arrows now detects non-interactive stdin and:
- resolves to default_key immediately when one is provided (making a
  fully scripted init expressible), or
- raises ValueError naming the missing interactive session when no
  default exists, instead of blocking forever.

Both call sites in init.py already guard with _stdin_is_interactive(),
so this is defense in depth: any future caller of the selector cannot
hang a non-interactive process again.

3 new tests: no-TTY without default raises, no-TTY with default
resolves, TTY path still interactive. 8/8 console tests green; no
regressions (test_cli failures are pre-existing in this environment,
identical count with and without this change).

Signed-off-by: Yunare Maia <yunare@gmail.com>
@yunaremaia
yunaremaia requested a review from mnriem as a code owner August 16, 2026 01:20
@yunaremaia

Copy link
Copy Markdown
Author

Hi maintainers — the workflow runs on this PR show action_required (first-time contributor policy). The change is small and self-contained:

Could you approve the runs when you get a chance? Thanks!

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.

specify init hangs on select_with_arrows prompts in non-interactive environments — no flag reaches them, --force does not bypass them

1 participant