fix: goose configure exits silently at the model picker#8790
fix: goose configure exits silently at the model picker#8790ahmadelafify wants to merge 1 commit into
Conversation
cliclack's spinner spawns a render thread that outlives `stop()`. When the next prompt opens stdin in raw mode immediately after `Model fetch complete`, the still-alive spinner thread races the picker's first read and `interact()?` propagates an error, exiting with code 2 before any key is processed. Drop the spinner explicitly and yield ~50ms before the model selector opens so cliclack tears the render thread down first. Fixes aaif-goose#8373.
b4bc964 to
7de9ad3
Compare
|
I don't think the diagnosis of the cause is quite right. In particular, "the spinner's render thread keeps writing to the terminal for a few more ms" cannot be true, because The root cause is a bug in the The real fix should be in the console crate, so I'll issue a PR there, but a robust workaround here is just to wait for the child process to be reaped before proceeding, instead of issuing the kill signal and immediately continuing without waiting. I'll open a PR here for that. |
|
Superseded by #9023 |
Fixes #8373.
Summary
On macOS 26.4 (M5 Pro, tested in kitty and Terminal.app),
goose configureexits with code 2 right afterModel fetch completeprints. The model picker draws one frame and disappears before any keypress is registered.The cause is a race between cliclack's spinner and the next prompt.
spin.stop()flips a flag and returns, but the spinner's render thread keeps writing to the terminal for a few more ms. The next line opens the model picker, which puts stdin in raw mode and reads. Both widgets fight over the terminal and the picker's first read returnsErr, which propagates out as exit 2.Fix: drop the spinner explicitly and yield 50ms so cliclack tears the render thread down before the picker starts reading. The user does not see the delay.
spin.stop(style("Model fetch complete").green()); + // cliclack's spinner render thread outlives `stop()` and races the next + // prompt's raw-mode stdin read; drop and yield so the picker's + // `interact()` doesn't fail before any key is read. See #8373. + drop(spin); + tokio::time::sleep(std::time::Duration::from_millis(50)).await;Testing
cargo build -p goose-cli --bin goosegoose configure→ Claude Code → picker stays open, arrow keys and enter work.Before:
https://github.com/user-attachments/assets/27d1d5b6-2d94-4f0b-9f0d-33d20fd7a52a
After:
https://github.com/user-attachments/assets/116f6d7c-84e8-4c6e-9845-81049615cc4b