{"record":{"id":"d0f935079b769d33","repo":"wavetermdev/waveterm","slug":"error-setting-terminal-size-w","errorCode":null,"errorMessage":"error setting terminal size: %w","messagePattern":"error setting terminal size: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobmanager/jobcmd.go","lineNumber":167,"sourceCode":"\tif jm.exitErr != nil {\n\t\texitData.ExitErr = jm.exitErr.Error()\n\t}\n\treturn true, exitData\n}\n\nfunc (jm *JobCmd) setTermSize_withlock(termSize waveobj.TermSize) error {\n\tif jm.cmdPty == nil {\n\t\treturn fmt.Errorf(\"no active pty\")\n\t}\n\tif jm.termSize.Rows == termSize.Rows && jm.termSize.Cols == termSize.Cols {\n\t\treturn nil\n\t}\n\terr := pty.Setsize(jm.cmdPty, &pty.Winsize{\n\t\tRows: uint16(termSize.Rows),\n\t\tCols: uint16(termSize.Cols),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error setting terminal size: %w\", err)\n\t}\n\tjm.termSize = termSize\n\treturn nil\n}\n\nfunc (jm *JobCmd) SetTermSize(termSize waveobj.TermSize) error {\n\tjm.lock.Lock()\n\tdefer jm.lock.Unlock()\n\treturn jm.setTermSize_withlock(termSize)\n}\n\n// TODO set up a single input handler loop + queue so we dont need to hold the lock but still get synchronized in-order execution\nfunc (jm *JobCmd) HandleInput(data wshrpc.CommandJobInputData) error {\n\tjm.lock.Lock()\n\tdefer jm.lock.Unlock()\n\n\tif jm.cmd == nil || jm.cmdPty == nil {\n\t\treturn fmt.Errorf(\"no active process\")","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobmanager/jobcmd.go#L149-L185","documentation":"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.","triggerScenarios":"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.","commonSituations":"Resizing a terminal right as its job exits; client sending extreme sizes (> 65535) that overflow the uint16 cast; stale PTY handle after process teardown.","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."],"exampleFix":"// before\nerr := pty.Setsize(jm.cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})\n// after\nif termSize.Rows <= 0 || termSize.Rows > 500 || termSize.Cols <= 0 || termSize.Cols > 500 {\n    return fmt.Errorf(\"term size out of range\")\n}\nerr := pty.Setsize(jm.cmdPty, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})","handlingStrategy":"validation","validationCode":"func clampTermSize(ts waveobj.TermSize) waveobj.TermSize {\n    if ts.Rows < 1 { ts.Rows = 1 }\n    if ts.Cols < 1 { ts.Cols = 1 }\n    if ts.Rows > 500 { ts.Rows = 500 }\n    if ts.Cols > 500 { ts.Cols = 500 }\n    return ts\n}","typeGuard":"func isResizableSize(ts waveobj.TermSize) bool {\n    return ts.Rows > 0 && ts.Rows <= 500 && ts.Cols > 0 && ts.Cols <= 500\n}","tryCatchPattern":"err := jobCmd.SetTermSize(termSize)\nif err != nil {\n    if strings.Contains(err.Error(), \"error setting terminal size\") {\n        if exited, _ := jobCmd.GetExitInfo(); exited {\n            return nil // pty closed due to exit; benign\n        }\n    }\n    return err\n}","preventionTips":["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"],"tags":["pty","terminal","ioctl","resize"],"backgroundTag":"pty-setsize-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}