{"record":{"id":"d2f80035b4f49ac4","repo":"wavetermdev/waveterm","slug":"no-active-pty","errorCode":null,"errorMessage":"no active pty","messagePattern":"no active pty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/jobmanager/jobcmd.go","lineNumber":157,"sourceCode":"\tdefer jm.lock.Unlock()\n\tif !jm.processExited {\n\t\treturn false, nil\n\t}\n\texitData := &wshrpc.CommandJobCmdExitedData{\n\t\tJobId:      WshCmdJobManager.JobId,\n\t\tExitCode:   jm.exitCode,\n\t\tExitSignal: jm.exitSignal,\n\t\tExitTs:     jm.exitTs,\n\t}\n\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()","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobmanager/jobcmd.go#L139-L175","documentation":"setTermSize_withlock resizes the job's PTY, but requires jm.cmdPty to be set. If the PTY handle is nil, no pty was started (or it was never assigned), and resizing is impossible, so this error is returned.","triggerScenarios":"SetTermSize or HandleInput (which resizes on first input) invoked on a JobCmd whose pty.StartWithSize never ran or failed — e.g. resizing a block whose job failed to launch, or before StartJob completes.","commonSituations":"Client sends a resize RPC for a block whose job crashed at startup; race where the UI resizes immediately after job creation but before the process starts.","solutions":["Ensure StartJob succeeded before issuing SetTermSize/input.","Check the job's running state before sending resize RPCs.","Handle the error gracefully in the client (ignore resize for non-running jobs).","Restart the job if the PTY was lost."],"exampleFix":"// before\nerr := jobCmd.SetTermSize(size)\n// after\nif !jobMgr.IsRunning() {\n    return nil // nothing to resize\n}\nerr := jobCmd.SetTermSize(size)","handlingStrategy":"validation","validationCode":"if !jobRunning { // confirm StartJob succeeded before resizing\n    return errors.New(\"skip resize: no job running\")\n}","typeGuard":"func canResize(jm *jobmanager.JobCmd) bool {\n    exited, _ := jm.GetExitInfo()\n    return !exited\n}","tryCatchPattern":"err := jobCmd.SetTermSize(termSize)\nif err != nil {\n    if err.Error() == \"no active pty\" {\n        return nil // job not started or already torn down; ignore resize\n    }\n    return err\n}","preventionTips":["Gate resize RPCs on a confirmed running job state","Ignore resize errors for blocks whose job failed to launch","Serialize startup and resize operations to avoid pre-start races"],"tags":["pty","terminal","resize","lifecycle"],"backgroundTag":"no-active-pty","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}