Environment
Description
reposcan TUI crashes with a recovered panic when opening repo details (uncommitted files view).
Caught panic:
runtime error: slice bounds out of range [:-573]
Stack trace
github.com/mabd-dev/reposcan/internal/render/tui/repodetails.(*Model).buildUncommittedFiles
internal/render/tui/repodetails/view.go:47
github.com/mabd-dev/reposcan/internal/render/tui/repodetails.(*Model).View
internal/render/tui/repodetails/view.go:25
github.com/mabd-dev/reposcan/internal/render/tui.Model.View
internal/render/tui/view.go:29
github.com/charmbracelet/bubbletea.(*Program).eventLoop
github.com/mabd-dev/reposcan/internal/render/tui.Render
internal/render/tui/main.go:87
Full panic:
Caught panic:
runtime error: slice bounds out of range [:-573]
Restoring terminal...
goroutine 1 [running]:
runtime/debug.Stack()
/opt/hostedtoolcache/go/1.24.13/x64/src/runtime/debug/stack.go:26 +0x5e
runtime/debug.PrintStack()
/opt/hostedtoolcache/go/1.24.13/x64/src/runtime/debug/stack.go:18 +0x13
github.com/charmbracelet/bubbletea.(*Program).recoverFromPanic(0xc00030c500, {0x96fdc0, 0xc000578d68})
/home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v1.3.6/tea.go:810 +0xac
github.com/charmbracelet/bubbletea.(*Program).Run.func2()
/home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v1.3.6/tea.go:601 +0xe8
panic({0x96fdc0?, 0xc000578d68?})
/opt/hostedtoolcache/go/1.24.13/x64/src/runtime/panic.go:792 +0x132
github.com/mabd-dev/reposcan/internal/render/tui/repodetails.(*Model).buildUncommittedFiles(0xc0010589b0)
/home/runner/work/reposcan/reposcan/internal/render/tui/repodetails/view.go:47 +0x6a7
github.com/mabd-dev/reposcan/internal/render/tui/repodetails.(*Model).View(0xc0010589b0)
/home/runner/work/reposcan/reposcan/internal/render/tui/repodetails/view.go:25 +0x457
github.com/mabd-dev/reposcan/internal/render/tui.Model.View({0x0, 0xba, 0x2e, {{{0xc0001fe9c0, 0x7}, {0xc0001fea88, 0x7}, {0xc0001feba8, 0x7}, {0xc0001fea30, ...}, ...}, ...}, ...})
/home/runner/work/reposcan/reposcan/internal/render/tui/view.go:29 +0x1d8
github.com/charmbracelet/bubbletea.(*Program).eventLoop(0xc00030c500, {0xab8648?, 0xc0007c2000?}, 0xc00074bc00)
/home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v1.3.6/tea.go:533 +0x892
github.com/charmbracelet/bubbletea.(*Program).Run(0xc00030c500)
/home/runner/go/pkg/mod/github.com/charmbracelet/bubbletea@v1.3.6/tea.go:679 +0xb56
github.com/mabd-dev/reposcan/internal/render/tui.Render({0x1, {0xc000afc000, 0x47, 0xc32}, 0xc32, {0xc29ffca540079a6c, 0x11228189c, 0xe9cda0}, {0xc000d81008, 0x73, ...}}, ...)
/home/runner/work/reposcan/reposcan/internal/render/tui/main.go:87 +0x785
github.com/mabd-dev/reposcan/cmd/reposcan.run({{0xc000022b40, 0x1, 0x1}, {0xc0001ca488, 0x29, 0x47}, {0x9a368b, 0x5}, {{0x9a6abf, 0xb}, ...}, ...})
/home/runner/work/reposcan/reposcan/cmd/reposcan/rootCmd.go:166 +0x1d8
github.com/mabd-dev/reposcan/cmd/reposcan.init.func2(0xdf77a0, {0x9a3263?, 0x4?, 0x9a31a7?})
/home/runner/work/reposcan/reposcan/cmd/reposcan/rootCmd.go:60 +0x398
github.com/spf13/cobra.(*Command).execute(0xdf77a0, {0xc0000220a0, 0x0, 0x0})
/home/runner/go/pkg/mod/github.com/spf13/cobra@v1.8.0/command.go:983 +0xad4
github.com/spf13/cobra.(*Command).ExecuteC(0xdf77a0)
/home/runner/go/pkg/mod/github.com/spf13/cobra@v1.8.0/command.go:1115 +0x44f
github.com/spf13/cobra.(*Command).Execute(...)
/home/runner/go/pkg/mod/github.com/spf13/cobra@v1.8.0/command.go:1039
github.com/mabd-dev/reposcan/cmd/reposcan.Execute()
/home/runner/work/reposcan/reposcan/cmd/reposcan/main.go:13 +0x1a
main.main()
/home/runner/work/reposcan/reposcan/main.go:8 +0xf
tui error: program was killed: program experienced a panic
Likely cause
In buildUncommittedFiles:
maxUncommitedFilesToShow := m.height - 3
trimUncommitedFiles := len(files) > m.height-3
if trimUncommitedFiles {
files = files[:maxUncommitedFilesToShow]
}
[:-573] means m.height is negative (~-570). Slice high bound is not clamped to >= 0.
Can happen if:
WindowSizeMsg arrives with height 0 / negative (resize, tmux, restore)
m.height is uninitialized before the first size message
- height is computed as
termHeight - header and underflows
Current if maxUncommitedFilesToShow <= 0 { return lines } on main may not be in the crashing build (panic line is view.go:47).
Suggested fix
Clamp before slicing, and do not slice with a negative high:
maxUncommitedFilesToShow := m.height - 3
if maxUncommitedFilesToShow < 0 {
maxUncommitedFilesToShow = 0
}
if maxUncommitedFilesToShow == 0 {
return nil
}
if maxUncommitedFilesToShow > len(files) {
maxUncommitedFilesToShow = len(files)
}
files = files[:maxUncommitedFilesToShow]
Also clamp m.height/m.width on tea.WindowSizeMsg (max(0, msg.Height)).
Guard f[:2] when a change line is shorter than 2 chars.
Expected
TUI does not panic on small/invalid terminal size; empty or truncated file list is fine.
Actual
Process panics, Bubble Tea recovers, TUI exits: program was killed: program experienced a panic.
Environment
Description
reposcan TUI crashes with a recovered panic when opening repo details (uncommitted files view).
Stack trace
Full panic:
Likely cause
In
buildUncommittedFiles:[:-573]meansm.heightis negative (~-570). Slice high bound is not clamped to>= 0.Can happen if:
WindowSizeMsgarrives with height0/ negative (resize, tmux, restore)m.heightis uninitialized before the first size messagetermHeight - headerand underflowsCurrent
if maxUncommitedFilesToShow <= 0 { return lines }onmainmay not be in the crashing build (panic line isview.go:47).Suggested fix
Clamp before slicing, and do not slice with a negative high:
Also clamp
m.height/m.widthontea.WindowSizeMsg(max(0, msg.Height)).Guard
f[:2]when a change line is shorter than 2 chars.Expected
TUI does not panic on small/invalid terminal size; empty or truncated file list is fine.
Actual
Process panics, Bubble Tea recovers, TUI exits:
program was killed: program experienced a panic.