charmbracelet / charmbracelet/fang
Interrupts while trying to read from stdin can cause the application to hang
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 48
- PR merge metrics
- No merged PRs in 30d
Description
### Describe the bug
Firstly, thanks for the great OSS project!
I'm building a CLI that reads from STDIN, and I'm trying to make sure it's cancelable using SIGINT. I observe that when I'm using Fang, there is a race condition when exiting that causes the application to hang indefinitely, maybe 50% of the time.
### Setup
Please complete the following information along with version numbers, if applicable.
- OS: MacOS 15.5
- Shell: zsh
- Terminal Emulator: ghostty
- Terminal Multiplexer: n/a
### To Reproduce
Steps to reproduce the behavior:
1. Run the sample code below.
2. Ctrl+C to close
3. Observe that frequently the application doesn't close, or closes slowly.
### Source Code
Here is a simplified example of my setup:
```go
package main
import (
"context"
"io"
"os"
"github.com/charmbracelet/fang"
"github.com/spf13/cobra"
)
func main() {
// NewRootCMD creates the root command for the epok CLI application.
rootCmd := &cobra.Command{
Use: "test",
RunE: handler,
}
fang.Execute(context.Background(), rootCmd,
fang.WithNotifySignal(os.Interrupt, os.Kill))
}
func handler(cmd *cobra.Command, _ []string) error {
data := make(chan string)
go func() {
in := cmd.InOrStdin()
_, _ = io.ReadAll(in) // Will block
}()
ctx := cmd.Context()
select {
case <-ctx.Done():
return ctx.Err()
case <-data:
return nil
}
}
```
### Expected behavior
I expect the application to consistently exit as it would w/o fang.
### Additional context
I traced the problem to [this line](https://github.com/charmbracelet/fang/blob/3d716d2d41181fd8193c51543d130e38e83860a5/theme.go#L118), which uses STDIN to determine the terminal background. I imagine there is contention reading from the same descriptor.
Assuming it's not user error, one solution that works for me is to pre-compute the styles in [Execute](https://github.com/charmbracelet/fang/blob/3d716d2d41181fd8193c51543d130e38e83860a5/fang.go#L114) before running the cobra command. If you think that's the right change, I'm happy to make a PR.
Contributor guide
Research direction
Start with the stdin access at theme.go line 118 and the style setup in fang.go around Execute at line 114. Reproduce the sample with a Cobra command that blocks in io.ReadAll, then investigate whether terminal-background detection races with the command's stdin read. Done means Ctrl+C consistently exits without hanging or slowing down.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100