{"record":{"id":"98bd99952d5355b5","repo":"wavetermdev/waveterm","slug":"error-writing-trailing-newline-to-output-adaptout","errorCode":null,"errorMessage":"error writing trailing newline to output (AdaptOutputChToStream): %w","messagePattern":"error writing trailing newline to output \\(AdaptOutputChToStream\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrpcio.go","lineNumber":40,"sourceCode":"\t}, readCallback)\n}\n\nfunc AdaptOutputChToStream(outputCh chan []byte, output io.Writer) error {\n\tdrain := false\n\tdefer func() {\n\t\tif drain {\n\t\t\tutilfn.DrainChannelSafe(outputCh, \"AdaptOutputChToStream\")\n\t\t}\n\t}()\n\tfor msg := range outputCh {\n\t\tif _, err := output.Write(msg); err != nil {\n\t\t\tdrain = true\n\t\t\treturn fmt.Errorf(\"error writing to output (AdaptOutputChToStream): %w\", err)\n\t\t}\n\t\t// write trailing newline\n\t\tif _, err := output.Write([]byte{'\\n'}); err != nil {\n\t\t\tdrain = true\n\t\t\treturn fmt.Errorf(\"error writing trailing newline to output (AdaptOutputChToStream): %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc AdaptMsgChToPty(outputCh chan []byte, oscEsc string, output io.Writer) error {\n\tif len(oscEsc) != 5 {\n\t\tpanic(\"oscEsc must be 5 characters\")\n\t}\n\tfor msg := range outputCh {\n\t\tbarr, err := EncodeWaveOSCBytes(oscEsc, msg)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error encoding osc message (AdaptMsgChToPty): %w\", err)\n\t\t}\n\t\tif _, err := output.Write(barr); err != nil {\n\t\t\treturn fmt.Errorf(\"error writing osc message (AdaptMsgChToPty): %w\", err)\n\t\t}\n\t}","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrpcio.go#L22-L58","documentation":"AdaptOutputChToStream writes a trailing newline after every message to frame them on the output stream. This error wraps a failure when writing that newline byte, meaning the underlying writer broke mid-message-stream even if the message body itself was written.","triggerScenarios":"The io.Writer returned an error specifically on the `output.Write([]byte{'\\n'})` call after a successful message write — typically a pipe that just closed or filled between the two writes.","commonSituations":"Output stream closes between message body and newline writes; disk-full when output is a file; a pty or pipe that errored after partial write.","solutions":["Inspect the wrapped error for the underlying write failure (EPIPE, ENOSPC).","Keep the output stream open for the lifetime of the channel pump.","Check available disk space if output is redirected to a file.","Treat as a broken consumer and shut down the producer gracefully."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if f, ok := output.(*os.File); ok {\n    if _, err := f.Stat(); err != nil {\n        return fmt.Errorf(\"output closed: %w\", err)\n    }\n}","typeGuard":"func isClosedFile(w io.Writer) bool {\n    f, ok := w.(*os.File)\n    if !ok { return false }\n    _, err := f.Stat()\n    return err != nil\n}","tryCatchPattern":"err := AdaptOutputChToStream(ch, output)\nif err != nil {\n    if errors.Is(err, syscall.EPIPE) { return nil }\n    return fmt.Errorf(\"stream write failed: %w\", err)\n}","preventionTips":["Keep output writable for the full lifetime of the pump","Monitor disk space when redirecting output to files","Drain channels only via the provided DrainChannelSafe helper"],"tags":["go","io","broken-pipe"],"backgroundTag":"broken-pipe-write-failure","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}