{"record":{"id":"8663c2f9e5bca71a","repo":"charmbracelet/bubbletea","slug":"bubbletea-error-closing-screen-writer-w","errorCode":null,"errorMessage":"bubbletea: error closing screen writer: %w","messagePattern":"bubbletea: error closing screen writer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cursed_renderer.go","lineNumber":223,"sourceCode":"\n\t\tif lv.BackgroundColor != nil {\n\t\t\t_, _ = s.scr.WriteString(ansi.ResetBackgroundColor)\n\t\t}\n\t\tif lv.ForegroundColor != nil {\n\t\t\t_, _ = s.scr.WriteString(ansi.ResetForegroundColor)\n\t\t}\n\t\tif lv.ProgressBar != nil && lv.ProgressBar.State != ProgressBarNone {\n\t\t\t_, _ = s.scr.WriteString(ansi.ResetProgressBar)\n\t\t}\n\t}\n\n\tif s.cellbuf.Method == ansi.GraphemeWidth {\n\t\t// Make sure to turn off Unicode mode (2027)\n\t\t_, _ = s.scr.WriteString(ansi.ResetModeUnicodeCore)\n\t}\n\n\tif err := s.scr.Flush(); err != nil {\n\t\treturn fmt.Errorf(\"bubbletea: error closing screen writer: %w\", err)\n\t}\n\n\tif s.buf.Len() > 0 {\n\t\tif s.logger != nil {\n\t\t\ts.logger.Printf(\"output: %q\", s.buf.String())\n\t\t}\n\t\tif _, err := io.Copy(s.w, &s.buf); err != nil {\n\t\t\treturn fmt.Errorf(\"bubbletea: error writing to screen: %w\", err)\n\t\t}\n\t\ts.buf.Reset()\n\t}\n\n\tx, y := s.scr.Position()\n\n\t// We want to clear the renderer state but not the cursor position. This is\n\t// because we might be putting the tea process in the background, run some\n\t// other process, and then return to the tea process. We want to keep the\n\t// cursor position so that we can continue where we left off.","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/charmbracelet/bubbletea/blob/351d2159f8d8a85613aa2a6e98c8c63df3c98623/cursed_renderer.go#L205-L241","documentation":"Returned by the cursed renderer's close path when the underlying screen writer's Flush fails while tearing down the renderer (Program shutting down or being closed). The close sequence writes terminal reset sequences (mouse modes, title, cursor style, colors, unicode mode 2027) and this error means those bytes could not be flushed to the output writer. It indicates the output channel (terminal, pty, or redirected writer) rejected writes at shutdown.","triggerScenarios":"Program shutdown when stdout is a closed pipe or pty; running with tea.WithOutput to a network connection that dropped; a terminal that was closed before the program exited; EPIPE/ErrClosed from the output file during the final Flush in renderer close.","commonSituations":"Piping output to `head` or another short-lived process that exits first; SSH session disconnected before graceful shutdown; test harness closing the fake output writer early; running a Bubble Tea program with output redirected to a file on a full disk.","solutions":["Ensure the output writer stays open until Program.Run returns","If piping/redirecting intentionally, use tea.WithoutRenderer() or tea.WithInput(nil) for non-TTY runs instead of a full TUI","Check for EPIPE and treat it as a benign shutdown error if the consumer is allowed to exit early","If over SSH, handle disconnects by cancelling the program context so shutdown happens while the pty is still alive"],"exampleFix":"// before\np := tea.NewProgram(m, tea.WithOutput(w))\n// w gets closed elsewhere while p.Run() is still shutting down\n\n// after\ndone := make(chan struct{})\ngo func() { p.Run(); close(done) }()\n// close w only after Run finished\ncloseWriterAfter(done)","handlingStrategy":"fallback","validationCode":"// Validate the output channel before shutdown can hit it:\nfunc isWritable(w io.Writer) bool {\n    if f, ok := w.(*os.File); ok {\n        return f.Fd() >= 0 // still has an fd\n    }\n    return true\n}","typeGuard":"func isClosedWrite(err error) bool {\n    return errors.Is(err, os.ErrClosed) || errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET)\n}","tryCatchPattern":"if err := runErr; err != nil {\n    if strings.Contains(err.Error(), \"error closing screen writer\") && isClosedWrite(errors.Unwrap(err)) {\n        return // consumer exited: benign\n    }\n    log.Print(err)\n}","preventionTips":["Close custom output writers only after Program.Run returns","For piped runs use tea.WithoutRenderer()","Add write-timeouts on custom writers so shutdown can't hang","Treat EPIPE on close-flush as expected in pipelines"],"tags":["bubbletea","renderer","flush","epipe","shutdown","go"],"backgroundTag":null,"analyzedSha":"351d2159f8d8a85613aa2a6e98c8c63df3c98623","analyzedAt":"2026-08-15T10:48:22.366Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}