wavetermdev/waveterm · error

cannot start job manager: %w

Error message

cannot start job manager: %w

What it means

cmd.Start() failed to launch the 'wsh jobmanager --jobid ... --clientid ...' child process. The error from os/exec (typically exec.ErrNotFound wrapped as 'exec: "wsh": executable file not found in $PATH', or a permission error) is wrapped with %w.

Source

Thrown at pkg/wshrpc/wshremote/wshremote_job.go:172

		cmd.Env = append(os.Environ(), "WAVETERM_PUBLICKEY="+data.PublicKeyBase64)
	}
	cmd.ExtraFiles = []*os.File{readyPipeWrite}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create stdin pipe: %w", err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create stdout pipe: %w", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, fmt.Errorf("cannot create stderr pipe: %w", err)
	}
	log.Printf("RemoteStartJobCommand: created pipes\n")

	if err := cmd.Start(); err != nil {
		return nil, fmt.Errorf("cannot start job manager: %w", err)
	}
	readyPipeWrite.Close()
	log.Printf("RemoteStartJobCommand: job manager process started\n")

	jobAuthTokenLine := fmt.Sprintf("Wave-JobAccessToken:%s\n", data.JobAuthToken)
	if _, err := stdin.Write([]byte(jobAuthTokenLine)); err != nil {
		cmd.Process.Kill()
		return nil, fmt.Errorf("cannot write job auth token: %w", err)
	}
	stdin.Close()
	log.Printf("RemoteStartJobCommand: wrote auth token to stdin\n")

	go func() {
		scanner := bufio.NewScanner(stderr)
		for scanner.Scan() {
			line := scanner.Text()
			log.Printf("RemoteStartJobCommand: stderr: %s\n", line)
		}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Run 'wsh init' / reinstall the wsh binary on the remote host so getWshPath() resolves a valid executable
  2. Check permissions: chmod +x on the wsh binary and verify the path it resolves to
  3. Verify the install filesystem is mounted and not noexec
  4. Reproduce by running the resolved wshPath manually with the jobmanager args

Example fix

// before
$ ls $(which wsh)
which: no wsh in (...)
// after
$ wsh init   # install the wsh binary on the remote host
$ chmod +x ~/.local/bin/wsh
Defensive patterns

Strategy: validation

Validate before calling

wshPath, err := getWshPath() // or replicate: exec.LookPath("wsh")
if err != nil {
    return fmt.Errorf("wsh binary not available: %w", err)
}
if info, err := os.Stat(wshPath); err != nil || info.Mode()&0o111 == 0 {
    return fmt.Errorf("wsh binary missing or not executable: %s", wshPath)
}

Type guard

func wshExecutableAvailable(path string) bool {
    info, err := os.Stat(path)
    return err == nil && !info.IsDir() && info.Mode()&0o111 != 0
}

Try / catch

rtn, err := server.RemoteStartJobCommand(ctx, data)
if err != nil {
    if strings.Contains(err.Error(), "cannot start job manager") {
        // reinstall wsh on the remote ('wsh init') and retry once
    }
    return err
}

Prevention

When it happens

Trigger: The resolved wshPath from getWshPath() points to a missing, deleted, or non-executable binary; the binary exists but lacks +x; or the filesystem housing it is unavailable.

Common situations: Wave not fully installed / wsh binary not installed on the remote host; PATH differences between server env and expected install location; binary removed by cleanup or an upgrade; noexec mount on the install directory.

Related errors


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