wavetermdev/waveterm · error
error setting terminal size: %w
Error message
error setting terminal size: %w
What it means
After checking for an active PTY, setTermSize_withlock calls pty.Setsize to apply the new window dimensions via TIOCSWINSZ. Any failure from that ioctl-style call (bad file descriptor because the process died, invalid size) is wrapped as this error.
Source
Thrown at pkg/jobmanager/jobcmd.go:167
if jm.exitErr != nil {
exitData.ExitErr = jm.exitErr.Error()
}
return true, exitData
}
func (jm *JobCmd) setTermSize_withlock(termSize waveobj.TermSize) error {
if jm.cmdPty == nil {
return fmt.Errorf("no active pty")
}
if jm.termSize.Rows == termSize.Rows && jm.termSize.Cols == termSize.Cols {
return nil
}
err := pty.Setsize(jm.cmdPty, &pty.Winsize{
Rows: uint16(termSize.Rows),
Cols: uint16(termSize.Cols),
})
if err != nil {
return fmt.Errorf("error setting terminal size: %w", err)
}
jm.termSize = termSize
return nil
}
func (jm *JobCmd) SetTermSize(termSize waveobj.TermSize) error {
jm.lock.Lock()
defer jm.lock.Unlock()
return jm.setTermSize_withlock(termSize)
}
// TODO set up a single input handler loop + queue so we dont need to hold the lock but still get synchronized in-order execution
func (jm *JobCmd) HandleInput(data wshrpc.CommandJobInputData) error {
jm.lock.Lock()
defer jm.lock.Unlock()
if jm.cmd == nil || jm.cmdPty == nil {
return fmt.Errorf("no active process")View on GitHub (pinned to a4447c1563)
Solutions
- Clamp Rows/Cols to a sane range (1..65535, realistically < 1000) before sending.
- Treat the error as 'process gone' if the job exited concurrently and suppress it.
- Unwrap and check for EBADF/ENXIO to confirm the PTY is dead, then clean up the job.
- Retry the resize once after confirming the job is still running.
Example fix
// before
err := pty.Setsize(jm.cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
// after
if termSize.Rows <= 0 || termSize.Rows > 500 || termSize.Cols <= 0 || termSize.Cols > 500 {
return fmt.Errorf("term size out of range")
}
err := pty.Setsize(jm.cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)}) Defensive patterns
Strategy: validation
Validate before calling
func clampTermSize(ts waveobj.TermSize) waveobj.TermSize {
if ts.Rows < 1 { ts.Rows = 1 }
if ts.Cols < 1 { ts.Cols = 1 }
if ts.Rows > 500 { ts.Rows = 500 }
if ts.Cols > 500 { ts.Cols = 500 }
return ts
} Type guard
func isResizableSize(ts waveobj.TermSize) bool {
return ts.Rows > 0 && ts.Rows <= 500 && ts.Cols > 0 && ts.Cols <= 500
} Try / catch
err := jobCmd.SetTermSize(termSize)
if err != nil {
if strings.Contains(err.Error(), "error setting terminal size") {
if exited, _ := jobCmd.GetExitInfo(); exited {
return nil // pty closed due to exit; benign
}
}
return err
} Prevention
- Clamp sizes to a realistic range to avoid uint16 overflow
- Suppress resize failures that race with process exit
- Unwrap and check for EBADF/ENXIO to detect dead ptys
When it happens
Trigger: pty.Setsize fails while resizing: the PTY master's slave side closed (process exited between the check and the call), an invalid file descriptor, or a uint16 overflow from enormous Rows/Cols values.
Common situations: Resizing a terminal right as its job exits; client sending extreme sizes (> 65535) that overflow the uint16 cast; stale PTY handle after process teardown.
Related errors
- no active pty
- failed to send input to job: %w
- invalid term size: %v
- error reading input: %w
- Cannot get last command data without shell integration
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/d0f935079b769d33.
Report an issue: GitHub.