Skip to content

fix: avoid Exec restore panic with WithInput(nil)#1680

Merged
aymanbagabas merged 1 commit intocharmbracelet:mainfrom
lawrence3699:fix/restore-terminal-nil-input
Apr 20, 2026
Merged

fix: avoid Exec restore panic with WithInput(nil)#1680
aymanbagabas merged 1 commit intocharmbracelet:mainfrom
lawrence3699:fix/restore-terminal-nil-input

Conversation

@lawrence3699
Copy link
Copy Markdown
Contributor

  • I have read CONTRIBUTING.md.
  • I have created a discussion that was approved by a maintainer (for new features).

Summary

Fix the WithInput(nil) + ExecProcess panic reported in the #761 discussion.

Problem

#761 recommends tea.WithInput(nil) as the workaround when there is no TTY available. That works until a program executes tea.ExecProcess(...) and returns to Bubble Tea.

At that point RestoreTerminal() always called initInputReader(false), even when input had been explicitly disabled. That rebuilds the cancel reader with p.input == nil and panics in cancelreader.

Minimal repro:

func (m model) Init() tea.Cmd {
    return tea.ExecProcess(exec.Command("true"), func(err error) tea.Msg {
        return finishedMsg{err}
    })
}

func main() {
    _, err := tea.NewProgram(model{}, tea.WithInput(nil)).Run()
    if err != nil {
        panic(err)
    }
}

Fix

Make RestoreTerminal() match the initial startup path in Run() and only reinitialize the input reader when p.input != nil.

Added TestTeaExecWithNilInput to cover the regression.

Validation

  • go test -run 'TestTeaExec|TestTeaExecWithNilInput' .
  • go test ./...

Copilot AI review requested due to automatic review settings April 19, 2026 12:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a panic when running ExecProcess in a program configured with WithInput(nil) by ensuring terminal restoration does not attempt to reinitialize the input reader when input is explicitly disabled.

Changes:

  • Update Program.RestoreTerminal() to only call initInputReader(false) when p.input != nil.
  • Add a regression test covering ExecProcess with WithInput(nil) to prevent reintroducing the panic.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
tea.go Prevents RestoreTerminal() from reinitializing the cancel reader when input is disabled (p.input == nil).
exec_test.go Adds a regression test that runs ExecProcess under WithInput(nil) to ensure no panic and no error.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread exec_test.go
return execFinishedMsg{err}
})
}

Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testExecNoInputModel embeds testExecModel, but it relies on the promoted Update method whose receiver is *testExecModel. That Update returns m (a *testExecModel), so after the first update the program’s model type changes from *testExecNoInputModel to *testExecModel. This is a bit brittle/confusing for a regression test; consider giving testExecNoInputModel its own Update/View (or avoid embedding) so it consistently returns the same concrete model type.

Suggested change
func (m *testExecNoInputModel) Update(msg Msg) (Model, Cmd) {
switch msg := msg.(type) {
case execFinishedMsg:
if msg.err != nil {
m.err = msg.err
}
return m, Quit
}
return m, nil
}
func (m *testExecNoInputModel) View() View {
return m.testExecModel.View()
}

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

❌ Patch coverage is 33.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.64%. Comparing base (fdcd0cf) to head (8e05899).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
tea.go 33.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1680      +/-   ##
==========================================
+ Coverage   55.38%   55.64%   +0.26%     
==========================================
  Files          25       25              
  Lines        1309     1310       +1     
==========================================
+ Hits          725      729       +4     
+ Misses        493      491       -2     
+ Partials       91       90       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aymanbagabas aymanbagabas merged commit 074596e into charmbracelet:main Apr 20, 2026
21 of 25 checks passed
@aymanbagabas
Copy link
Copy Markdown
Member

@lawrence3699 Thank you so much!

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.

3 participants