Skip to content

CLI elicitation readline races with background chat-input reader on stdin, causing client-side timeout #6398

Description

@bug-ops

Description

Found live during #6388's fix verification (fixing AppChannel never forwarding elicit() to the real CliChannel/TuiChannel implementations). Once elicitation requests actually reach CliChannel::elicit() (previously unreachable — see #6388), a second, separate bug surfaces: CliChannel's persistent background chat-input reader (run_tty_reader, crates/zeph-channels/src/cli.rs:261-269) races with elicit()'s own blocking per-field readline (crates/zeph-channels/src/cli.rs:545, via spawn_blocking) for the same stdin/terminal, with no coordination between the two concurrent readers.

This bug was structurally unreachable before #6388 landed — elicit() was never invoked at all in the real binary, so the race could never trigger. It may have existed silently since MCP elicitation shipped.

Reproduction Steps

  1. Apply the MCP server-driven elicitation always auto-declines — AppChannel never forwards elicit() #6388 fix (or check out its branch) so AppChannel::elicit() actually forwards to CliChannel::elicit().
  2. Build a minimal stdio MCP server that sends a real elicitation/create request mid-tools/call (the CI-1411 fixture server works: .local/testing/scripts/fake_mcp_elicit_server_ci1411.py + .local/config/ci1411-elicitation.toml).
  3. Run cargo run --features full -- --config .local/config/ci1411-elicitation.toml (plain CLI).
  4. Trigger the tool call that causes the elicitation request; the [MCP server '...' is requesting input] banner and confirm [true/false]: prompt appear correctly.
  5. Type a response at the prompt.
  6. Observe: the typed input is consumed by the background chat-input reader instead of the elicitation readline, and the round trip ends in a client-side elicitation timeout (~60s) instead of completing with the typed value.

Expected Behavior

While an elicitation prompt is active, stdin input should route to the elicitation readline exclusively; the background chat-input reader should yield/pause until the elicitation response is resolved.

Actual Behavior

Both readers compete for stdin concurrently with no coordination, so the elicitation response is frequently/always stolen by the wrong reader, causing a 60s client-side timeout instead of a completed round trip.

Environment

See Also

#6388 (where this was incidentally discovered during live fix-verification, not caused by that fix — it made the pre-existing race reachable for the first time)

Metadata

Metadata

Assignees

Labels

P2High value, medium complexitybugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions