wavetermdev/waveterm · error
error writing to output (AdaptOutputChToStream): %w
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.
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)
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 trailing newline to output (AdaptOutputChToStr
- error writing osc message (AdaptMsgChToPty): %w
- reading from stdin: %w
- reading input: %w
- failed to copy data: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/8f55ab920fbdfe57.
Report an issue: GitHub.