{"record":{"id":"80d47df6ed8027f2","repo":"charmbracelet/bubbletea","slug":"program-was-interrupted","errorCode":null,"errorMessage":"program was interrupted","messagePattern":"program was interrupted","errorType":"error_code","errorClass":"ErrInterrupted","httpStatus":null,"severity":"info","filePath":"tea.go","lineNumber":46,"sourceCode":"\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/charmbracelet/colorprofile\"\n\tuv \"github.com/charmbracelet/ultraviolet\"\n\t\"github.com/charmbracelet/x/ansi\"\n\t\"github.com/charmbracelet/x/term\"\n\t\"github.com/muesli/cancelreader\"\n)\n\n// ErrProgramPanic is returned by [Program.Run] when the program recovers from a panic.\nvar ErrProgramPanic = errors.New(\"program experienced a panic\")\n\n// ErrProgramKilled is returned by [Program.Run] when the program gets killed.\nvar ErrProgramKilled = errors.New(\"program was killed\")\n\n// ErrInterrupted is returned by [Program.Run] when the program get a SIGINT\n// signal, or when it receives a [InterruptMsg].\nvar ErrInterrupted = errors.New(\"program was interrupted\")\n\n// Msg contain data from the result of a IO operation. Msgs trigger the update\n// function and, henceforth, the UI.\ntype Msg = uv.Event\n\n// Model contains the program's state as well as its core functions.\ntype Model interface {\n\t// Init is the first function that will be called. It returns an optional\n\t// initial command. To not perform an initial command return nil.\n\tInit() Cmd\n\n\t// Update is called when a message is received. Use it to inspect messages\n\t// and, in response, update the model and/or send a command.\n\tUpdate(Msg) (Model, Cmd)\n\n\t// View renders the program's UI, which can be a string or a [Layer]. The\n\t// view is rendered after every Update.\n\tView() View","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/charmbracelet/bubbletea/blob/351d2159f8d8a85613aa2a6e98c8c63df3c98623/tea.go#L28-L64","documentation":"ErrInterrupted is returned by Program.Run when the program received SIGINT (Ctrl+C) or an explicit tea.InterruptMsg, and exited because of it. It distinguishes a user-initiated interrupt from a graceful quit (which returns nil) so callers can decide exit codes. It is only produced when the built-in signal handler is active (not disabled with WithoutSignalHandler).","triggerScenarios":"User presses Ctrl+C while the program owns the terminal in raw mode; the process receives SIGINT from the shell or a supervisor; the model sends tea.InterruptMsg (e.g. via a tea.Interrupt command) to terminate with this status.","commonSituations":"CLI tools wanting exit code 130 on Ctrl+C; programs where Ctrl+C should mean 'cancel current operation' but the default handler quits; running under debuggers or supervisors that deliver SIGINT; shells sending SIGINT to whole process groups.","solutions":["If Ctrl+C should be handled by the model instead of exiting, filter it in Update on KeyMsg with tea.KeyCtrlC and return tea.Quit yourself (Run then returns nil)","If you want the interrupt status, check errors.Is(err, tea.ErrInterrupted) after Run and map it to exit code 130","Use tea.WithoutSignalHandler() and wire your own signal handling if you need custom SIGINT semantics","Do not treat this error as a bug — it is a normal termination signal"],"exampleFix":"// before\nif _, err := p.Run(); err != nil {\n    log.Fatal(err) // treats Ctrl+C as fatal\n}\n\n// after\n_, err := p.Run()\nif err != nil && !errors.Is(err, tea.ErrInterrupted) {\n    log.Fatal(err)\n}\nos.Exit(130)","handlingStrategy":"try-catch","validationCode":"// If you need custom Ctrl+C behavior, install no changes here — handle KeyMsg\n// in Update and don't let the default quit path surprise you:\n// p := tea.NewProgram(m) // default signal handler active","typeGuard":"func isInterrupted(err error) bool {\n    return err != nil && errors.Is(err, tea.ErrInterrupted)\n}","tryCatchPattern":"_, err := p.Run()\nif isInterrupted(err) {\n    os.Exit(130) // conventional SIGINT exit status\n}\nif err != nil { log.Fatal(err) }","preventionTips":["Map ErrInterrupted to exit code 130 in CLIs for shell-friendliness","To intercept Ctrl+C, match tea.KeyCtrlC in Update and return your own Cmd/Quit","Remember ErrInterrupted only appears with the built-in signal handler enabled","Don't conflate interrupted (user action) with killed (programmatic)"],"tags":["bubbletea","signal","sigint","lifecycle","go"],"backgroundTag":null,"analyzedSha":"351d2159f8d8a85613aa2a6e98c8c63df3c98623","analyzedAt":"2026-08-15T10:48:22.366Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}