Skip to content

gh aw hangs at startup when invoked under a pty/pipe wrapper on Windows (speculative) #37806

Description

@jamesadevine

Observation

When gh aw <subcommand> is invoked from inside a wrapper that captures the child process's stdout/stderr through a pseudo-terminal or pipe layer, the binary appears to hang indefinitely before producing any output, even for commands that normally complete in seconds (e.g. gh aw version, gh aw compile).

Direct invocation in an interactive terminal works fine. Wrapping the call in a PowerShell background job — which spawns a separate pwsh.exe whose stdio is a plain serialized pipe rather than the wrapper's pty — also works fine.

I'm filing this speculatively: I haven't bisected or instrumented gh-aw itself, just observed the symptom and tested a workaround. Posting in case it helps someone who hits the same thing, or in case the root cause is obvious to a maintainer.

How to reproduce (one observation, not a confirmed minimal repro)

Environment where I saw it:

  • Windows 11 (10.0.26200.0)
  • PowerShell 7.6.2
  • gh v2.92.0
  • gh aw v0.77.5 (installed as a gh CLI extension)
  • Caller: an agent runtime (GitHub Copilot CLI) that captures child stdout via a wrapper layer; the same symptom would presumably reproduce under any other pty/pipe-wrapping caller (CI step capturing output, IDE task runner, etc.).

In that environment:

# Hangs indefinitely with no output; had to Stop-Process to recover.
& "$env:USERPROFILE\AppData\Local\GitHub CLI\extensions\gh-aw\gh-aw.exe" version

Workaround that completes immediately in the same environment:

$exe = "$env:USERPROFILE\AppData\Local\GitHub CLI\extensions\gh-aw\gh-aw.exe"
$job = Start-Job -ScriptBlock { param($e) & $e version 2>&1 } -ArgumentList $exe
Wait-Job $job -Timeout 30 | Out-Null
Receive-Job $job
# -> "gh aw version v0.77.5"

Hypotheses (unverified)

I haven't confirmed any of these — listing them in case one rings a bell:

  1. TTY probe on startup. Many Go CLIs that use Bubble Tea / lipgloss / mattn/go-isatty probe stdout on startup to decide whether to render styled output. On Windows that often involves GetConsoleMode / GetConsoleScreenBufferInfo, which can interact poorly with non-console handles inherited from a wrapper. If the probe blocks rather than returning an error in that case, it could explain "no output, no exit" with no obvious failure surface in the CLI itself.

  2. Bubble Tea program waiting on input. If a default tea.Program is being started even for non-interactive commands like version (e.g. via a shared initializer), and the program is configured to read stdin, a wrapper that hasn't closed/redirected stdin properly could leave the program parked waiting for a keypress. This would also explain why a background-job re-spawn (different inherited handles) clears it.

  3. Synchronous write to a stderr/stdout that's flow-control-blocked by the wrapper. Less likely for a version command, but conceivable if there's banner output emitted before the command dispatch and the wrapper has back-pressured the pipe.

The fact that gh aw version (which presumably does no real work) also hangs makes me suspect (1) or (2) — something on the initialization path rather than something in any specific subcommand. But again, this is speculation.

What might help downstream users regardless of root cause

If the maintainers wanted to make this less surprising without necessarily fixing the underlying interaction, a few things would help:

  • A --no-tty / --plain flag (or honoring NO_COLOR=1 / CI=true / TERM=dumb) that skips any console probe and bypasses Bubble Tea entirely for non-interactive subcommands like version, compile, etc.
  • A startup timeout on the TTY probe itself, so the process fails fast with a clear error instead of hanging silently.
  • A note in the troubleshooting docs that pty-wrapping callers should use a background subprocess (or --plain, if added) — this would have saved me an afternoon.

I'm happy to do more digging (e.g. attach a debugger to see where exactly the process is parked, or test against an older gh aw version) if a maintainer can confirm this is worth chasing rather than a known quirk.

Workaround for others who hit this

Wrap the call in Start-Job:

$exe = "$env:USERPROFILE\AppData\Local\GitHub CLI\extensions\gh-aw\gh-aw.exe"
$job = Start-Job -ScriptBlock { param($e, $cwd) Set-Location $cwd; & $e compile 2>&1 } `
  -ArgumentList $exe, (Get-Location).Path
Wait-Job $job -Timeout 300 | Out-Null
Receive-Job $job

This spawns a fresh pwsh.exe whose stdio handles are not the wrapper's pty, so whatever startup-time interaction was deadlocking the direct invocation doesn't trigger.

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions