charmbracelet/bubbletea · critical · ErrProgramPanic
program experienced a panic
Error message
program experienced a panic
What it means
ErrProgramPanic is a sentinel error returned by Program.Run when the framework recovered from a panic that occurred inside your Model's Init, Update, View, or a Cmd. Bubble Tea catches panics by default (unless WithoutCatchPanics is set) so the terminal state is restored before the process dies. The actual panic value is logged and printed via p.recoverFromPanic, while Run returns an error wrapping this sentinel (combined with ErrProgramKilled).
Source
Thrown at tea.go:39
"os/signal"
"runtime"
"runtime/debug"
"strconv"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/charmbracelet/colorprofile"
uv "github.com/charmbracelet/ultraviolet"
"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/term"
"github.com/muesli/cancelreader"
)
// ErrProgramPanic is returned by [Program.Run] when the program recovers from a panic.
var ErrProgramPanic = errors.New("program experienced a panic")
// ErrProgramKilled is returned by [Program.Run] when the program gets killed.
var ErrProgramKilled = errors.New("program was killed")
// ErrInterrupted is returned by [Program.Run] when the program get a SIGINT
// signal, or when it receives a [InterruptMsg].
var ErrInterrupted = errors.New("program was interrupted")
// Msg contain data from the result of a IO operation. Msgs trigger the update
// function and, henceforth, the UI.
type Msg = uv.Event
// Model contains the program's state as well as its core functions.
type Model interface {
// Init is the first function that will be called. It returns an optional
// initial command. To not perform an initial command return nil.
Init() Cmd
View on GitHub (pinned to 351d2159f8)
Solutions
- Look at the program output/stderr: recoverFromPanic prints the panic message and stack trace — fix the line it points to
- Temporarily run the program with tea.WithoutCatchPanics() option so the panic crashes with a full goroutine dump
- Reproduce in `go test` by calling Update/View directly with the message that triggered the panic
- Add nil/bounds guards for the state the panic references
- If the panic comes from inside the library itself, file an issue at github.com/charmbracelet/bubbletea with the stack trace
Example fix
// before
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m.items[m.sel], nil // panics when m.sel >= len(m.items)
}
// after
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.sel >= len(m.items) {
return m, nil
}
return m.items[m.sel], nil
} Defensive patterns
Strategy: try-catch
Validate before calling
// before p.Run(): nothing to validate; panics surface at runtime.
// Optionally build the program with panic capture ON (default) and a logger:
// f, _ := tea.LogToFile("debug.log", "debug")
// p := tea.NewProgram(m, tea.WithLogger(log.New(f, "", log.LstdFlags))) Type guard
func isProgramPanic(err error) bool {
return err != nil && errors.Is(err, tea.ErrProgramPanic)
} Try / catch
model, err := p.Run()
if err != nil {
if isProgramPanic(err) {
// terminal already restored; stack trace printed by recoverFromPanic
os.Exit(2)
}
log.Fatal(err)
} Prevention
- Keep Model constructors total: never return nil models or partially initialized state
- Write table-driven Update tests for every message type you handle
- Guard slice/map access coming from user input or WindowSizeMsg
- Run with a logger attached (tea.WithLogger + LogToFile) so panics are captured with traces
- Use tea.WithoutCatchPanics only during debugging to get raw dumps
When it happens
Trigger: Any panic inside code invoked by the event loop: nil pointer dereference in Update, index out of range in View, nil map writes, division by zero in a Cmd, or a panic in a method called during rendering. Only fires when Program.Run is executing and p.disableCatchPanics is false (the default).
Common situations: A Model field not initialized before first Update; a slice indexed by user input without bounds checks; a nil sub-model after a state transition; panics inside a Cmd goroutine that bubble up through the cmds channel handling; upgrading Bubble Tea major versions where View/Update signatures changed and a half-migrated model panics.
Related errors
- %w: %w
- program was killed
- program was interrupted
- bubbletea: InitialModel cannot be nil
- bubbletea: error closing screen writer: %w
AI-assisted analysis of charmbracelet/bubbletea@351d2159f8 (2026-08-15).
Data as JSON: /api/errors/60e3e75735f20033.
Report an issue: GitHub.