wavetermdev/waveterm · error
error writing to output (AdaptOutputChToStream)
Error message
error writing to output (AdaptOutputChToStream): %w
What it means
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.
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.
Example fix
// before
func caller() {
AdaptOutputChToStream(ch, os.Stdout)
}
// after
func caller() error {
err := AdaptOutputChToStream(ch, os.Stdout)
if err != nil && errors.Is(err, syscall.EPIPE) {
return nil // consumer closed pipe; exit cleanly
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go has no pre-write validation; check writer usability
if f, ok := output.(*os.File); ok {
if _, err := f.Stat(); err != nil {
return fmt.Errorf("output unusable: %w", err)
}
} Type guard
func isUsableWriter(w io.Writer) bool {
c, ok := w.(io.Closer)
return !ok || c != nil // caller must track lifecycle; inspect concrete type as needed
} Try / catch
err := AdaptOutputChToStream(ch, output)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EPIPE) {
return nil // consumer gone; handle quietly
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Downstream consumer (process reading the pipe) exits early; CLI output redirected to a closed stream; EPIPE/Broken pipe when the parent process is killed.
Related errors
- error writing osc message (AdaptMsgChToPty)
- error writing trailing newline to output…
- Error reading request body
- failed to copy data
- failed to copy data: (stderr: )
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/8f55ab920fbdfe57.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/wshutil/wshrpcio.go:35
// * websocket (json packets)
func AdaptStreamToMsgCh(input io.Reader, output chan baseds.RpcInputChType, readCallback func()) error {
return utilfn.StreamToLines(input, func(line []byte) {
output <- baseds.RpcInputChType{MsgBytes: line}
}, readCallback)
}
func AdaptOutputChToStream(outputCh chan []byte, output io.Writer) error {
drain := false
defer func() {
if drain {
utilfn.DrainChannelSafe(outputCh, "AdaptOutputChToStream")
}
}()
for msg := range outputCh {
if _, err := output.Write(msg); err != nil {
drain = true
return fmt.Errorf("error writing to output (AdaptOutputChToStream): %w", err)
}
// write trailing newline
if _, err := output.Write([]byte{'\n'}); err != nil {
drain = true
return fmt.Errorf("error writing trailing newline to output (AdaptOutputChToStream): %w", err)
}
}
return nil
}
func AdaptMsgChToPty(outputCh chan []byte, oscEsc string, output io.Writer) error {
if len(oscEsc) != 5 {
panic("oscEsc must be 5 characters")
}
for msg := range outputCh {
barr, err := EncodeWaveOSCBytes(oscEsc, msg)
if err != nil {
return fmt.Errorf("error encoding osc message (AdaptMsgChToPty): %w", err)View on GitHub (pinned to a4447c1563)