Skip to content

fix(zsh): stop the spinner from running shell-exit cleanup - #76

Merged
lacymorrow merged 1 commit into
mainfrom
fix/spinner-freeze
Sep 29, 2026
Merged

lacymorrow merged 1 commit into
mainfrom
fix/spinner-freeze

Conversation

@lacymorrow

@lacymorrow lacymorrow commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Queries could freeze with the spinner stuck mid-frame. Every query also left a stopped background job behind, which showed up as a climbing job count in the prompt (✦2).

Cause. In v1.8.21 (c63b017), cleanup moved from trap … EXIT to a zshexit hook. zsh also runs zshexit hooks when a subshell calls exit, and the spinner does that on SIGTERM. So lacy_stop_spinner was running lacy_shell_cleanup inside the spinner. That cleanup restored MONITOR, so its first fork (command rm) got its own process group and was stopped by SIGTTOU. The spinner then never exited. On v1.8.21 to v1.8.23 the stop path waited on it forever, freezing the shell. Since v1.8.24 the stuck spinner gets abandoned as a stopped job instead.

Fix

  • lacy_shell_cleanup returns early unless ZSH_SUBSHELL == 0.
  • The spinner subshell clears zshexit_functions, so no plugin's exit hook runs there.

Verified

  • New test in test_zsh_adapter.zsh ("stopping the spinner ends it"). It fails on main (SPINNER=alive) and passes with the fix.
  • script/test.sh: 19/19 suites pass (bash, zsh, fish, installer).
  • A pty repro with a stubbed lash (server never comes up, falls back to single-shot) left a stopped job on every run of main (2/2) and of the installed v1.8.21 (3/3). With the fix: 0/4, and the answer prints.

A query could freeze with the spinner stuck mid-frame, and each one left a
stopped background job (the prompt's job count kept climbing).

Since v1.8.21 cleanup is a zshexit hook instead of an EXIT trap. zsh also
runs zshexit hooks when a subshell calls `exit`, and the spinner does that
on SIGTERM. So every lacy_stop_spinner ran lacy_shell_cleanup inside the
spinner: it restored MONITOR, the next fork (`command rm`) took its own
process group and stopped on SIGTTOU, and the spinner never exited. Before
v1.8.24 the stop path then blocked in `wait` forever; after it, the stuck
spinner was abandoned as a stopped job.

- lacy_shell_cleanup returns early outside the main shell (ZSH_SUBSHELL)
- the spinner clears zshexit_functions so no plugin's exit hook runs there
- test_zsh_adapter: stopping the spinner must end it (fails before the fix)
@lacymorrow
lacymorrow merged commit 2298366 into main Sep 29, 2026
13 checks passed
@lacymorrow
lacymorrow deleted the fix/spinner-freeze branch September 29, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant