charmbracelet / charmbracelet/fang

Interrupts while trying to read from stdin can cause the application to hang

Open
#60 3 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.