Skip to content

fix(cli): Add requireTTY() before each unguarded interactive TUI la... (#982)#41

Draft
aidandaly24 wants to merge 4 commits into
mainfrom
fix/982
Draft

fix(cli): Add requireTTY() before each unguarded interactive TUI la... (#982)#41
aidandaly24 wants to merge 4 commits into
mainfrom
fix/982

Conversation

@aidandaly24

Copy link
Copy Markdown
Owner

Refs aws#982

Issues

  • Non-TTY deploy crashes with Ink stack trace and exits 0 aws/agentcore-cli#982 — Running an interactive TUI command in a non-TTY context (piped stdin, CI, backgrounded) crashes with Ink's "Raw mode is not supported on the current process.stdin" stack trace. For agentcore import (no --source) the process then exits 0, so CI/scripts cannot detect the failure (the exit-0 variant in the issue). For agentcore view <type> and agentcore export harness the same launch is unguarded. The literal title reproducer (agentcore deploy) is already fixed at v0.20.2, but the underlying bug class persists in import/view/export.

Root cause

Interactive Ink TUIs render screens whose useInput hook calls setRawMode(true), which throws on non-TTY stdin (ink App.js:117, exact match to the issue text). requireTTY() (src/cli/tui/guards/tty.ts:8-13, added PR aws#949 / commit c30ed54) guards deploy/create/add/remove/invoke/dev/exec/run/feedback but NOT import/view/export. Verified at v0.20.2: deploy/command.tsx:218,221 are guarded (the original claim that deploy is the gap is wrong); the unguarded interactive launches are import/command.ts:67 (raw render of ImportFlow), view/command.tsx:97-121 (launchTuiList/launchTuiDetail render history screens that use useListNavigation->useInput), export/index.ts:32 (renderTUI export-harness). Import's render() is never awaited via waitUntilExit(), so Ink's ErrorBoundary (App.js:347) swallows the throw and the process exits 0; export awaits renderTUI->waitUntilExit() so its rejection propagates to exit 1 but with the ugly Ink stack trace. index.ts:9-16 only catches uncaughtException/unhandledRejection, neither of which fires for the import no-op rejectExitPromise path.

The fix

Add requireTTY() before each unguarded interactive launch, mirroring PR aws#949: import (src/cli/commands/import/command.ts no-source branch before requireProject()/render), view (src/cli/commands/view/command.tsx before launchTuiList/launchTuiDetail), and export (src/cli/commands/export/index.ts before renderTUI at :32). Open PR aws#1170 is OPEN and "Closes aws#982" but only patches import/command.ts (7 lines) plus tty/import/deploy tests — its own "Out of scope" section confirms it does NOT cover view or export, so merging it would close the issue while leaving two sites with the identical bug. Recommend expanding aws#1170 to also guard view and export, or more robustly centralizing the guard: call requireTTY() at the top of renderTUI() (src/cli/tui/render.ts:35) AND at the raw render() interactive sites so any future interactive launch is covered by default.

Files touched: src/cli/commands/import/command.ts:26-29,67-73 (no-source interactive branch; partially addressed by open PR aws#1170); src/cli/commands/view/command.tsx:97-121 (NOT covered by aws#1170); src/cli/commands/export/index.ts:32 (NOT covered by aws#1170); guard def src/cli/tui/guards/tty.ts:8-13; ideal central fix point src/cli/tui/render.ts:35. Deploy already guarded at src/cli/commands/deploy/command.tsx:218,221 (no change needed).

Validation evidence

The fix was verified by reproducing the original symptom and re-running after the change:

BEFORE (rebuilt unpatched HEAD binary at dist/cli/index.mjs, non-TTY via echo "" | ...): (1) WORST CASE agentcore import (no --source) inside a valid agentcore project rendered the Ink ImportFlow TUI menu and EXITED 0 — failure undetectable to CI/scripts. (2) agentcore export harness rendered the TUI and printed the literal "Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default." to stderr. (3) agentcore view recommendation and agentcore view recommendation rec-1 (inside valid project so requireProject passed) rendered the loading TUI screen and EXITED 0. All three unguarded render()/renderTUI() sites were reached, confirming the gap. Verified HEAD has 0 requireTTY calls in import/command.ts, view/command.tsx, export/index.ts.
AFTER (rebuilt fix/982): all four invocations (import in-project, view list, view detail, export harness) exit code 1, print ONLY the plain-text "Error: This command requires an interactive terminal. Use --help to see non-interactive flags." with NO "Raw mode" output and NO exit 0; render()/renderTUI() is never reached. requireTTY() (checks both process.stdin.isTTY and process.stdout.isTTY, console.error + process.exit(1)) is placed before render(ImportFlow) at import/command.ts:29, at the top of launchTuiList (view/command.tsx:98) and launchTuiDetail (:118), and before renderTUI at export/index.ts:33 — matching the proven deploy guard (deploy/command.tsx:222,225, PR aws#949). 4/4 manual checks passed.

Test suite: green.


Staged on the fork as a draft for human review. Promote to aws/agentcore-cli after vetting.

)

Non-TTY interactive TUI commands crashed with Ink's 'Raw mode is not
supported on the current process.stdin' and, for import (no --source),
exited 0 so CI/scripts could not detect the failure. deploy was already
guarded by requireTTY (PR aws#949); import/view/export were not.

Add requireTTY() before each unguarded interactive launch:
- export/index.ts: before renderTUI for export-harness
- import/command.ts: in the no-source branch before render(ImportFlow)
- view/command.tsx: at the top of launchTuiList and launchTuiDetail

requireProject()/--json paths untouched. Adds unit tests covering the
three command suites.

Refs aws#982
@github-actions github-actions Bot added size/m PR size: M agentcore-harness-reviewing AgentCore Harness review in progress and removed agentcore-harness-reviewing AgentCore Harness review in progress labels Jun 25, 2026
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 37.35% 13667 / 36582
🔵 Statements 36.62% 14528 / 39672
🔵 Functions 31.96% 2345 / 7336
🔵 Branches 31.2% 9029 / 28932
Generated in workflow #129 for commit cee06c4 by the Vitest Coverage Report Action

- centralize the interactive-terminal guard at the top of renderTUI() so
  every TUI entrypoint (export-harness and any future renderTUI caller) is
  covered by default; keep inline guards only on the raw render() sites
  (import, view)
- rewrite the three tty-guard tests to exercise the real requireTTY by
  toggling process.stdin.isTTY/process.stdout.isTTY and spying on
  process.exit + console.error, mocking only the I/O boundaries
  (mirrors feedback/consent-prompt.test.ts) instead of mocking requireTTY
- parameterize the view list guard over all four job types
- make requireTTY-vs-requireProject ordering consistent: requireProject()
  first in import (matches view)
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Jun 25, 2026
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Jun 25, 2026
launchTuiDevScreenWithPicker called render() without a requireTTY() guard, so in a non-TTY context it threw Ink's "Raw mode is not supported" stack trace plus a partial alt-screen paint — a counterexample to the centralized guard PR aws#1640 added. Call requireTTY() at the top of the picker (before the alt-screen write and render) and add a tty-guard test that drives the real guard for this path.
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-TTY deploy crashes with Ink stack trace and exits 0

1 participant