wavetermdev/waveterm · error
invalid term size: %v
Error message
invalid term size: %v
What it means
MakeJobCmd validates the terminal size before spawning the job's command under a PTY. After substituting the default 25x80 when both are zero, any remaining non-positive Rows or Cols is rejected. This prevents passing an impossible window size to pty.StartWithSize.
Source
Thrown at pkg/jobmanager/jobcmd.go:53
cleanedUp bool
ptyClosed bool
processExited bool
exitCode *int
exitSignal string
exitErr error
exitTs int64
}
func MakeJobCmd(jobId string, cmdDef CmdDef) (*JobCmd, error) {
jm := &JobCmd{
jobId: jobId,
}
if cmdDef.TermSize.Rows == 0 || cmdDef.TermSize.Cols == 0 {
cmdDef.TermSize.Rows = 25
cmdDef.TermSize.Cols = 80
}
if cmdDef.TermSize.Rows <= 0 || cmdDef.TermSize.Cols <= 0 {
return nil, fmt.Errorf("invalid term size: %v", cmdDef.TermSize)
}
ecmd := exec.Command(cmdDef.Cmd, cmdDef.Args...)
if len(cmdDef.Env) > 0 {
ecmd.Env = make([]string, 0, len(cmdDef.Env))
for key, val := range cmdDef.Env {
ecmd.Env = append(ecmd.Env, fmt.Sprintf("%s=%s", key, val))
}
}
cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(cmdDef.TermSize.Rows), Cols: uint16(cmdDef.TermSize.Cols)})
if err != nil {
return nil, fmt.Errorf("failed to start command: %w", err)
}
unixutil.SetCloseOnExec(int(cmdPty.Fd()))
jm.cmd = ecmd
jm.cmdPty = cmdPty
jm.ptsName = jm.cmdPty.Name()
jm.termSize = cmdDef.TermSize
go jm.waitForProcess()View on GitHub (pinned to a4447c1563)
Solutions
- Ensure the caller supplies a positive TermSize (Rows > 0 and Cols > 0) in CommandJobStartData.
- If no real size is known, omit it (both 0) so the default 25x80 is applied.
- Clamp client-reported sizes before sending the RPC (max(1, size)).
- Validate TermSize at the RPC boundary with a guard before calling StartJob.
Example fix
// before
cmdDef.TermSize = waveobj.TermSize{Rows: -1, Cols: 80}
// after
cmdDef.TermSize = waveobj.TermSize{Rows: 25, Cols: 80} // or leave zeroed to get the default Defensive patterns
Strategy: validation
Validate before calling
func validTermSize(ts waveobj.TermSize) bool {
if ts.Rows == 0 && ts.Cols == 0 {
return true // defaults will be applied
}
return ts.Rows > 0 && ts.Cols > 0
} Type guard
func isPositiveSize(r, c int) bool { return r > 0 && c > 0 } Try / catch
cmd, err := jobmanager.MakeJobCmd(jm, cmdDef)
if err != nil {
if strings.Contains(err.Error(), "invalid term size") {
cmdDef.TermSize = waveobj.TermSize{Rows: 25, Cols: 80}
cmd, err = jobmanager.MakeJobCmd(jm, cmdDef)
}
if err != nil { return err }
} Prevention
- Validate TermSize at the RPC boundary before calling StartJob
- Send both Rows and Cols together or neither
- Clamp client-reported sizes to positive values before RPC
When it happens
Trigger: Starting a job (StartJob -> MakeJobCmd) with a CommandJobStart TermSize whose Rows or Cols is negative (or one is zero while the other isn't, so the zero-default doesn't apply).
Common situations: A client sending a partially-populated TermSize (e.g. Rows set, Cols zero, or negative values from a deserialization bug); a caller constructing CommandJobStartData manually without a valid TermSize.
Related errors
- block %s is not a terminal block (view type: %s)
- count must be positive
- failed to send input to job: %w
- no active pty
- error setting terminal size: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/fbc9706986298149.
Report an issue: GitHub.