wavetermdev/waveterm · warning
process already exited
Error message
process already exited
What it means
GetPGID tracks job termination via the processExited flag (set by the exit-waiter). Once the process has exited, its pgid is no longer meaningful/available, so the method refuses the lookup with this error.
Source
Thrown at pkg/jobmanager/jobcmd.go:125
log.Printf("process exited: exitcode=%s, signal=%s, err=%v\n", exitCodeStr, jm.exitSignal, jm.exitErr)
go WshCmdJobManager.sendJobExited()
}
func (jm *JobCmd) GetCmd() (*exec.Cmd, pty.Pty) {
jm.lock.Lock()
defer jm.lock.Unlock()
return jm.cmd, jm.cmdPty
}
func (jm *JobCmd) GetPGID() (int, error) {
jm.lock.Lock()
defer jm.lock.Unlock()
if jm.cmd == nil || jm.cmd.Process == nil {
return 0, fmt.Errorf("no active process")
}
if jm.processExited {
return 0, fmt.Errorf("process already exited")
}
pgid, err := unixutil.GetProcessGroupId(jm.cmd.Process.Pid)
if err != nil {
return 0, fmt.Errorf("failed to get pgid: %w", err)
}
if pgid <= 0 {
return 0, fmt.Errorf("invalid pgid returned: %d", pgid)
}
return pgid, nil
}
func (jm *JobCmd) GetExitInfo() (bool, *wshrpc.CommandJobCmdExitedData) {
jm.lock.Lock()
defer jm.lock.Unlock()
if !jm.processExited {
return false, nil
}
exitData := &wshrpc.CommandJobCmdExitedData{View on GitHub (pinned to a4447c1563)
Solutions
- Check GetExitInfo() before calling GetPGID and skip if exited.
- Listen for the job-exited event instead of polling the pgid.
- Treat this error as a benign 'already done' condition in callers.
- Re-fetch fresh job state after restart if the pgid is genuinely needed.
Example fix
// before
pgid, err := jobCmd.GetPGID()
// after
exited, _ := jobCmd.GetExitInfo()
if exited {
return nil // job already finished
}
pgid, err := jobCmd.GetPGID() Defensive patterns
Strategy: validation
Validate before calling
exited, exitData := jobCmd.GetExitInfo()
if exited {
return fmt.Errorf("job exited (code %v); pgid unavailable", exitData)
} Type guard
func isRunning(jm *jobmanager.JobCmd) bool {
exited, _ := jm.GetExitInfo()
return !exited
} Try / catch
pgid, err := jobCmd.GetPGID()
if err != nil {
if err.Error() == "process already exited" {
return nil // benign: treat as completed job
}
return err
} Prevention
- Subscribe to the job-exited event instead of polling the pgid
- Check GetExitInfo before any signal/pgid operation
- Treat 'process already exited' as a success path in cleanup logic
When it happens
Trigger: Calling GetPGID after the job's process has terminated — e.g. querying the process group to send a signal but the command already finished (or was killed) and the exit watcher flipped processExited.
Common situations: Sending SIGTERM to a job that already completed; a UI still calling process-management RPCs on a finished block; race between process exit and a cleanup routine fetching the pgid.
Related errors
- no active process
- Layout model not found
- Display container not found
- procinfo: process not found
- error starting controller: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/59b5a038353c3151.
Report an issue: GitHub.