wavetermdev/waveterm · error
failed to start command: %w
Error message
failed to start command: %w
What it means
After building the exec.Command, MakeJobCmd calls pty.StartWithSize to spawn the process attached to a pseudo-terminal. Any failure from that call (command not found, permission denied, PTY allocation exhaustion) is wrapped as this error.
Source
Thrown at pkg/jobmanager/jobcmd.go:64
jobId: jobId,
}
if cmdDef.TermSize.Rows == 0 || cmdDef.TermSize.Cols == 0 {
cmdDef.TermSize.Rows = 25
cmdDef.TermSize.Cols = 80
}
if cmdDef.TermSize.Rows <= 0 || cmdDef.TermSize.Cols <= 0 {
return nil, fmt.Errorf("invalid term size: %v", cmdDef.TermSize)
}
ecmd := exec.Command(cmdDef.Cmd, cmdDef.Args...)
if len(cmdDef.Env) > 0 {
ecmd.Env = make([]string, 0, len(cmdDef.Env))
for key, val := range cmdDef.Env {
ecmd.Env = append(ecmd.Env, fmt.Sprintf("%s=%s", key, val))
}
}
cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(cmdDef.TermSize.Rows), Cols: uint16(cmdDef.TermSize.Cols)})
if err != nil {
return nil, fmt.Errorf("failed to start command: %w", err)
}
unixutil.SetCloseOnExec(int(cmdPty.Fd()))
jm.cmd = ecmd
jm.cmdPty = cmdPty
jm.ptsName = jm.cmdPty.Name()
jm.termSize = cmdDef.TermSize
go jm.waitForProcess()
return jm, nil
}
func (jm *JobCmd) waitForProcess() {
if jm.cmd == nil || jm.cmd.Process == nil {
return
}
err := jm.cmd.Wait()
jm.lock.Lock()
defer jm.lock.Unlock()
View on GitHub (pinned to a4447c1563)
Solutions
- Unwrap the error and check for exec.ErrNotFound / 'permission denied'; fix the Cmd path or chmod +x the binary.
- Verify the command exists in PATH within the job manager's environment.
- Check pty availability: raise ulimit / kernel pty limits if 'out of pty' errors appear.
- Retry StartJob after fixing the environment if it was transient resource exhaustion.
Example fix
// before Cmd: "node serve.js" // after Cmd: "/usr/local/bin/node", Args: ["serve.js"] // absolute path to an executable binary
Defensive patterns
Strategy: validation
Validate before calling
// verify the binary is executable before starting the job
if _, err := exec.LookPath(cmdDef.Cmd); err != nil {
return fmt.Errorf("command %q not found in PATH: %w", cmdDef.Cmd, err)
} Type guard
func cmdStartable(cmd string, args []string) bool {
_, err := exec.LookPath(cmd)
return err == nil
} Try / catch
cmdPty, err := pty.StartWithSize(ecmd, size)
if err != nil {
if errors.Is(err, exec.ErrNotFound) || errors.Is(err, os.ErrPermission) {
// surface a clear 'bad command' message to the user
}
return fmt.Errorf("failed to start command: %w", err)
} Prevention
- Use absolute paths or verify the command resolves via exec.LookPath before StartJob
- Check ulimit -u / pty availability in containerized environments
- Ensure the binary has the executable bit and correct interpreter shebang
When it happens
Trigger: StartJob with a Cmd that doesn't exist in PATH, isn't executable, an Env list that breaks exec, or the OS can't allocate a new PTY (out of file descriptors / no free ptys).
Common situations: Typo in the command name in the job start RPC; running in a container without PTY support or with a low ulimit on ptys; command binary missing after a deployment change.
Related errors
- failed to start process: %w
- cannot start job manager: %w
- procinfo: process not found
- error starting controller: %w
- failed to start durable shell: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/9131a7bf192b5b8c.
Report an issue: GitHub.