wavetermdev/waveterm · error

no active process

Error message

no active process

What it means

GetPGID requires a live OS process handle on the job's command. If jm.cmd or jm.cmd.Process is nil, no process was ever started (or the handle was never populated), so the lookup cannot proceed and this error is returned.

Source

Thrown at pkg/jobmanager/jobcmd.go:122

	if jm.exitCode != nil {
		exitCodeStr = fmt.Sprintf("%d", *jm.exitCode)
	}
	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 {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Only call GetPGID after StartJob reports success.
  2. Check the job's running/exited status before querying the pgid.
  3. Guard the call site: skip pgid lookup when the job was never started.
  4. Recreate/restart the job if the handle was lost.

Example fix

// before
pgid, err := jobCmd.GetPGID()
// after
if !jobMgr.IsRunning() {
    return 0, errors.New("job not started")
}
pgid, err := jobCmd.GetPGID()
Defensive patterns

Strategy: validation

Validate before calling

// ensure the job was started successfully before querying pgid
if !jobStartedSuccessfully { // track via StartJob result
    return errors.New("cannot get pgid: job never started")
}

Type guard

func jobQueryable(jm *jobmanager.JobCmd) bool {
    _, err := jm.GetExitInfo()
    return err == nil // job object is live and queryable
}

Try / catch

pgid, err := jobCmd.GetPGID()
if err != nil {
    if err.Error() == "no active process" {
        return 0, errors.New("job was never started; call StartJob first")
    }
    return 0, err
}

Prevention

When it happens

Trigger: Calling GetPGID on a JobCmd before StartJob/MakeJobCmd succeeded, or after the command struct exists but pty.StartWithSize failed so jm.cmd.Process was never set.

Common situations: A client querying the process group of a job that failed to launch; a race where GetPGID is called between JobCmd creation and successful start; job object restored from state without a live process.

Related errors


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