Skip to content

TUI hangs at 'Loading: N skills' on NFS/GPFS: SIGCHLD race when Tokio spawns 'which gh' subprocess with 30+ concurrent threads #4053

Description

@raylim

Summary

In TUI mode on Linux with home directories on GPFS/NFS, the CLI hangs indefinitely at "Loading: N skills". The binary stops logging almost immediately after startup and never becomes responsive. The hang occurs even with no MCP servers configured.

Symptom

The log line gh CLI availability check completed never appears. MCP clients never start. The binary displays "Loading: N skills" forever.

Hanging run (GPFS, heavy concurrent I/O):
T+0s   Starting Copilot CLI: 1.0.69
T+∞    Loading: 1 instruction, N skills   ← stuck
       [gh CLI check callback never fires, MCP clients never start]

Working run:
T+0.09s  Registering foreground session
T+0.27s  gh CLI availability check completed
T+1.04s  Starting MCP clients...
T+5-20s  AI response received ✓

Environment

  • Linux (RHEL 8), home directory on GPFS or NFS
  • Hangs intermittently without any MCP configuration; hangs more reliably when command:-based MCP servers that exit immediately are configured (e.g. misconfigured MCPs that crash on startup)
  • Not reproducible on macOS (Darwin uses kqueue for child-process events, not signalfd)

What is known

During startup, the binary spawns a which gh subprocess (via clone(SIGCHLD)) to check if the gh CLI is available. On GPFS systems with many concurrent I/O threads, the callback for this subprocess completion never fires. Without it, MCP clients never start and the TUI hangs.

strace -qq -e trace=none -f -o /dev/null wrapping the binary consistently and immediately fixes the hang. The -f flag (attach ptrace to all threads via PTRACE_O_TRACECLONE) is required — without it the hang persists. This strongly suggests the issue is related to how SIGCHLD is delivered across threads under heavy I/O load on Linux.

The suspected mechanism is a race in libuv's signalfd-based child-process tracking: SIGCHLD from the which gh subprocess is missed when all Tokio worker threads are busy with concurrent GPFS I/O. However, this has not been confirmed at the kernel level — the exact reason strace -f fixes it is inferred, not directly observed.

Having command:-based MCP servers that exit immediately (crashed/misconfigured MCPs) makes the hang more likely to occur, but is not required — the hang can occur with no MCP configuration at all under sufficient GPFS load.

Workaround

#!/bin/bash
# ~/bin/copilot wrapper
exec strace -qq -e trace=none -f -o /dev/null /path/to/copilot-linux-x64/copilot "$@"

Zero syscall-tracing overhead. The -f flag is required.

Suggested fix

  1. Add a timeout/fallback for the gh CLI check — if the subprocess does not complete within ~500ms, assume gh is unavailable and continue startup. This would prevent the hang regardless of the signal delivery issue.
  2. Decouple MCP client startup from the gh CLI check — MCP initialization should not be gated on an optional subprocess check. Starting MCP clients unconditionally would eliminate the hang.
  3. Investigate libuv signalfd behavior on Linux under high thread/I/O load — specifically whether SIGCHLD from short-lived subprocesses can be silently dropped when all threads are busy.

Option 1 or 2 would be the most impactful: a hung subprocess check should never be able to prevent the TUI from becoming usable.

Version

  • Copilot CLI: 1.0.69
  • OS: Linux (RHEL 8), home directory on GPFS
  • Not reproducible on macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:mcpMCP server configuration, discovery, connectivity, OAuth, policy, and registryarea:platform-linuxLinux-specific: Wayland, X11, Ubuntu, Fedora, Alpine, ARM, terminal emulatorstriagedThis issue has been added to our internal issue tracker

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions