wavetermdev/waveterm · error

error reading input

Error message

error reading input: %w

What it means

PtyBuffer.run reads from the underlying PTY/pipe reader in a loop and hands bytes to processData. If a read returns an error other than io.EOF, the buffer stores it wrapped as "error reading input: %w" and stops. Anyone reading from the PtyBuffer (MakePtyBuffer consumers) subsequently receives this error, meaning the terminal data stream terminated abnormally rather than cleanly at EOF.

Solutions

  1. Inspect the wrapped inner error (%w) — on Linux an EIO from a PTY read after the child exits usually means the process ended; treat it as end-of-stream if exit status is already captured.
  2. Check the state of the process attached to the PTY (wsh ps / ps aux) and restart it if it died unexpectedly.
  3. Ensure the reader backing the PtyBuffer is valid and not closed by another goroutine; close the buffer itself when done to avoid reading a dead fd.
  4. If this happens reproducibly on session exit, upgrade Wave Terminal — PTY teardown handling (EIO-as-EOF) is a common PTY library fix.

Example fix

// before: reading from a PtyBuffer whose PTY died, surfacing raw error
data, err := ptyBuffer.Read(ctx) // error reading input: read /dev/ptmx: input/output error
// after: tolerate EIO-at-exit as normal termination
data, err := ptyBuffer.Read(ctx)
if err != nil && strings.Contains(err.Error(), "input/output error") && cmdDone {
    err = io.EOF // process already exited; treat as end of stream
}
Defensive patterns

Strategy: try-catch

Try / catch

data, err := ptyBuffer.Read(ctx)
if err != nil {
    var ptyErr error
    if errors.As(err, &ptyErr) && strings.Contains(err.Error(), "error reading input") {
        if isEioAfterExit(err) { // treat EIO after child exit as EOF
            return io.EOF
        }
        return fmt.Errorf("pty stream failed: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: The PTY master/child side is closed abruptly (process killed, file descriptor invalid), an I/O error occurs on the underlying reader (EIO after the child exits on Linux PTYs), or the source file/pipe is closed while MakePtyBuffer is still reading.

Common situations: Shell process crashes or is OOM-killed while output is being streamed; terminal block reading a PTY whose child exited causing EIO instead of EOF; container/SSH sessions dropping mid-stream.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/21b20ea9db93cce9. Report an issue: GitHub.

Appendix: source

Thrown at pkg/wshutil/wshcmdreader.go:83

	b.CVar.Broadcast()
}

func (b *PtyBuffer) processWaveEscSeq(escSeq []byte) {
	b.MessageCh <- baseds.RpcInputChType{MsgBytes: escSeq}
}

func (b *PtyBuffer) run() {
	defer close(b.MessageCh)
	buf := make([]byte, 4096)
	for {
		n, err := b.InputReader.Read(buf)
		b.processData(buf[:n])
		if err == io.EOF {
			b.setEOF()
			return
		}
		if err != nil {
			b.setErr(fmt.Errorf("error reading input: %w", err))
			return
		}
	}
}

func (b *PtyBuffer) processData(data []byte) {
	outputBuf := make([]byte, 0, len(data))
	for _, ch := range data {
		if b.EscMode == Mode_WaveEsc {
			if ch == ESC {
				// terminates the escape sequence (and the rest was invalid)
				b.EscMode = Mode_Normal
				outputBuf = append(outputBuf, b.EscSeqBuf...)
				outputBuf = append(outputBuf, ch)
				b.EscSeqBuf = nil
			} else if ch == BEL || ch == ST {
				// terminates the escpae sequence (is a valid Wave OSC command)
				b.EscMode = Mode_Normal

View on GitHub (pinned to a4447c1563)