Description of the issue
Currently when running tests for https://github.com/max-sixty/worktrunk/ with nextest, the test process gets backgrounded. I've worked with Claude to get good attribution for this, and a reproduction (it wrote the words below, but I stand behind them).
nextest receives SIGTTOU and gets suspended when a test spawns an interactive shell subprocess (zsh -ic or bash -ic). The issue triggers during InputHandler cleanup when the interactive subprocess causes nextest to lose foreground process group status.
Minimal reproduction:
git clone https://github.com/max-sixty/nextest-sigttou-repro
cd nextest-sigttou-repro
cargo nextest run --test minimal
The test simply spawns an interactive shell:
Command::new("zsh")
.args(["-ic", "echo hello"])
.output()
What triggers it:
zsh -ic "..." (interactive zsh)
bash -ic "..." (interactive bash)
- Any binary that internally spawns an interactive shell
What doesn't trigger it:
- Non-interactive shells:
zsh -c "...", bash -c "..."
- Simple binaries (echo, cargo, etc.)
- Tests run with
cargo test (no InputHandler)
- Non-interactive terminals (CI, pipes)
Root cause:
- InputHandler modifies terminal settings at startup (disables echo, canonical mode)
- Test spawns
zsh -ic which starts an interactive shell
- The interactive shell takes control of the terminal's foreground process group
- When the subprocess exits, nextest is no longer in the foreground process group
- During cleanup,
InputHandlerImpl::restore calls tcsetattr()
tcsetattr() on a background process triggers SIGTTOU
- Process is suspended
The foreground check at startup (added in 0.9.94) doesn't prevent this because the process group status changes during execution.
Suggested fix: Block SIGTTOU during tcsetattr() in InputHandler cleanup (common pattern used by less, vim, etc.)
Expected outcome
Tests complete normally without nextest being suspended.
Actual result
zsh: suspended (tty output) cargo nextest run --test minimal
When resumed with fg:
failed to restore terminal state: failed to restore terminal state
Nextest version
cargo-nextest 0.9.114 (ff4ee9ac9 2025-11-19)
release: 0.9.114
commit-hash: ff4ee9ac9e7feb312e2ba6ea2d5eec40142a2ce0
commit-date: 2025-11-19
host: aarch64-apple-darwin
Additional context
Workaround: NEXTEST_NO_INPUT_HANDLER=1 cargo nextest run
Environment: macOS 14.x, zsh terminal (also affects Linux)
Description of the issue
Currently when running tests for https://github.com/max-sixty/worktrunk/ with
nextest, the test process gets backgrounded. I've worked with Claude to get good attribution for this, and a reproduction (it wrote the words below, but I stand behind them).nextest receives SIGTTOU and gets suspended when a test spawns an interactive shell subprocess (
zsh -icorbash -ic). The issue triggers during InputHandler cleanup when the interactive subprocess causes nextest to lose foreground process group status.Minimal reproduction:
git clone https://github.com/max-sixty/nextest-sigttou-repro cd nextest-sigttou-repro cargo nextest run --test minimalThe test simply spawns an interactive shell:
What triggers it:
zsh -ic "..."(interactive zsh)bash -ic "..."(interactive bash)What doesn't trigger it:
zsh -c "...",bash -c "..."cargo test(no InputHandler)Root cause:
zsh -icwhich starts an interactive shellInputHandlerImpl::restorecallstcsetattr()tcsetattr()on a background process triggers SIGTTOUThe foreground check at startup (added in 0.9.94) doesn't prevent this because the process group status changes during execution.
Suggested fix: Block SIGTTOU during
tcsetattr()in InputHandler cleanup (common pattern used by less, vim, etc.)Expected outcome
Tests complete normally without nextest being suspended.
Actual result
When resumed with
fg:Nextest version
Additional context
Workaround:
NEXTEST_NO_INPUT_HANDLER=1 cargo nextest runEnvironment: macOS 14.x, zsh terminal (also affects Linux)