wavetermdev/waveterm · error

invalid term size: %v

Error message

invalid term size: %v

What it means

StartWslShellProcNoWsh validates the terminal size before starting the pty. After defaulting zero-valued Rows/Cols to shellutil.DefaultTermRows/DefaultTermCols, it rejects sizes that are still <= 0 with "invalid term size: %v". This prevents passing nonsensical Winsize values to pty.StartWithSize.

Source

Thrown at pkg/shellexec/shellexec.go:166

	return err2
}

func (pp *PipePty) WriteString(s string) (n int, err error) {
	return pp.Write([]byte(s))
}

func StartWslShellProcNoWsh(ctx context.Context, termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *wslconn.WslConn) (*ShellProc, error) {
	client := conn.GetClient()
	conn.Infof(ctx, "WSL-NEWSESSION (StartWslShellProcNoWsh)")

	ecmd := exec.Command("wsl.exe", "~", "-d", client.Name())

	if termSize.Rows == 0 || termSize.Cols == 0 {
		termSize.Rows = shellutil.DefaultTermRows
		termSize.Cols = shellutil.DefaultTermCols
	}
	if termSize.Rows <= 0 || termSize.Cols <= 0 {
		return nil, fmt.Errorf("invalid term size: %v", termSize)
	}
	cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
	if err != nil {
		return nil, err
	}
	cmdWrap := MakeCmdWrap(ecmd, cmdPty, true)
	return &ShellProc{Cmd: cmdWrap, ConnName: conn.GetName(), CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil
}

func StartWslShellProc(ctx context.Context, termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *wslconn.WslConn) (*ShellProc, error) {
	if cmdOpts.SwapToken == nil {
		return nil, fmt.Errorf("SwapToken is required in CommandOptsType")
	}
	client := conn.GetClient()
	conn.Infof(ctx, "WSL-NEWSESSION (StartWslShellProc)")
	connRoute := wshutil.MakeConnectionRouteId(conn.GetName())
	rpcClient := wshclient.GetBareRpcClient()
	remoteInfo, err := wshclient.RemoteGetInfoCommand(rpcClient, &wshrpc.RpcOpts{Route: connRoute, Timeout: 2000})

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure Rows/Cols are positive before calling, or pass 0 to use defaults
  2. Sanitize the TermSize: clamp negative values to defaults instead of passing them through
  3. Fix the resize event/state source producing negative dimensions

Example fix

// before
ts := waveobj.TermSize{Rows: -1, Cols: 30}
proc, err := shellexec.StartWslShellProcNoWsh(ctx, ts, cmdStr, cmdOpts, conn)
// after
if ts.Rows <= 0 { ts.Rows = shellutil.DefaultTermRows }
if ts.Cols <= 0 { ts.Cols = shellutil.DefaultTermCols }
proc, err := shellexec.StartWslShellProcNoWsh(ctx, ts, cmdStr, cmdOpts, conn)
Defensive patterns

Strategy: validation

Validate before calling

if termSize.Rows < 0 { termSize.Rows = shellutil.DefaultTermRows }
if termSize.Cols < 0 { termSize.Cols = shellutil.DefaultTermCols }

Type guard

func validTermSize(ts waveobj.TermSize) bool {
    return ts.Rows >= 0 && ts.Cols >= 0 // 0 means use defaults
}

Try / catch

proc, err := shellexec.StartWslShellProcNoWsh(ctx, termSize, cmdStr, cmdOpts, conn)
if err != nil && strings.Contains(err.Error(), "invalid term size") {
    termSize = waveobj.TermSize{Rows: shellutil.DefaultTermRows, Cols: shellutil.DefaultTermCols}
    proc, err = shellexec.StartWslShellProcNoWsh(ctx, termSize, cmdStr, cmdOpts, conn)
}

Prevention

When it happens

Trigger: Calling StartWslShellProcNoWsh with a waveobj.TermSize whose Rows or Cols is negative (0 is auto-corrected to defaults, negatives are not), e.g. a termSize built from garbage/corrupt block state.

Common situations: Term size persisted as negative values after a malformed resize event; caller constructing TermSize manually with invalid dimensions.

Related errors


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