{"record":{"id":"8f55ab920fbdfe57","repo":"wavetermdev/waveterm","slug":"error-writing-to-output-adaptoutputchtostream","errorCode":null,"errorMessage":"error writing to output (AdaptOutputChToStream): %w","messagePattern":"error writing to output \\(AdaptOutputChToStream\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrpcio.go","lineNumber":35,"sourceCode":"// * websocket (json packets)\n\nfunc AdaptStreamToMsgCh(input io.Reader, output chan baseds.RpcInputChType, readCallback func()) error {\n\treturn utilfn.StreamToLines(input, func(line []byte) {\n\t\toutput <- baseds.RpcInputChType{MsgBytes: line}\n\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)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrpcio.go#L17-L53","documentation":"AdaptOutputChToStream pumps messages from a channel to an io.Writer (usually a stdout pipe). This error wraps any write failure from the underlying writer, aborting the pump loop. The library then drains the remaining channel to avoid blocking producers.","triggerScenarios":"The io.Writer passed to AdaptOutputChToStream returns an error on Write — e.g. a pipe whose read end is closed, a closed stdout, or a full/closed file descriptor. Returned from within the `for msg := range outputCh` loop.","commonSituations":"Downstream consumer (process reading the pipe) exits early; CLI output redirected to a closed stream; EPIPE/Broken pipe when the parent process is killed.","solutions":["Check the wrapped %w error for the underlying cause (e.g. EPIPE means the reader closed).","Ensure the reader of the output stream stays open until the channel is fully consumed.","Handle Broken pipe specially in CLI code (exit quietly, like standard Unix tools).","Verify output is not closed or finalized before AdaptOutputChToStream finishes."],"exampleFix":"// before\nfunc caller() {\n    AdaptOutputChToStream(ch, os.Stdout)\n}\n// after\nfunc caller() error {\n    err := AdaptOutputChToStream(ch, os.Stdout)\n    if err != nil && errors.Is(err, syscall.EPIPE) {\n        return nil // consumer closed pipe; exit cleanly\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Go has no pre-write validation; check writer usability\nif f, ok := output.(*os.File); ok {\n    if _, err := f.Stat(); err != nil {\n        return fmt.Errorf(\"output unusable: %w\", err)\n    }\n}","typeGuard":"func isUsableWriter(w io.Writer) bool {\n    c, ok := w.(io.Closer)\n    return !ok || c != nil // caller must track lifecycle; inspect concrete type as needed\n}","tryCatchPattern":"err := AdaptOutputChToStream(ch, output)\nif err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EPIPE) {\n        return nil // consumer gone; handle quietly\n    }\n    return err\n}","preventionTips":["Keep the reader of the output stream alive until the channel closes","Handle SIGPIPE / EPIPE gracefully in CLI tools","Avoid closing os.Stdout early in parent processes"],"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"}