hashicorp/nomad · error

failed to start command: %v

Error message

failed to start command: %v

What it means

UniversalExecutor.runTTY wraps any failure from e.processStart() (which calls the underlying exec.Cmd Start) with this message. It means the command binary could not be started after the TTY was configured. The wrapped error from the OS/exec package carries the real cause.

Source

Thrown at drivers/shared/executor/exec_utils.go:59

func (e *execHelper) run(ctx context.Context, tty bool, stream drivers.ExecTaskStream) error {
	if tty {
		return e.runTTY(ctx, stream)
	}
	return e.runNoTTY(ctx, stream)
}

func (e *execHelper) runTTY(ctx context.Context, stream drivers.ExecTaskStream) error {
	ptyF, tty, err := e.newTerminal()
	if err != nil {
		return fmt.Errorf("failed to open a tty: %v", err)
	}
	defer tty.Close()

	if err := e.setTTY(tty); err != nil {
		return fmt.Errorf("failed to set command tty: %v", err)
	}
	if err := e.processStart(); err != nil {
		return fmt.Errorf("failed to start command: %v", err)
	}

	var wg sync.WaitGroup
	errCh := make(chan error, 3)

	pty, err := ptyF()
	if err != nil {
		return fmt.Errorf("failed to get pty: %v", err)
	}

	defer pty.Close()
	wg.Add(1)
	go handleStdin(e.logger, pty, stream, errCh)
	// when tty is on, stdout and stderr point to the same pty so only read once
	go handleStdout(e.logger, pty, &wg, stream.Send, errCh)

	ps, err := e.processWait()

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check the wrapped %v cause: 'no such file or directory' means fix the command path in the task config
  2. Verify the binary exists and is executable inside the task environment (chmod +x, correct chroot/image)
  3. Confirm binary architecture matches the host (exec format error)
  4. Check ulimits (nproc) and cgroup PID limits on the host
  5. Run the same command manually as the nomad user to reproduce

Example fix

// before
command = "myapp-binary" // not present in task dir
// after
command = "/usr/local/bin/myapp" // absolute path to an existing executable
Defensive patterns

Strategy: validation

Validate before calling

func validateCommand(path string) error {
  fi, err := os.Stat(path)
  if err != nil { return fmt.Errorf("command not found: %w", err) }
  if fi.IsDir() || fi.Mode()&0o111 == 0 { return fmt.Errorf("%s is not executable", path) }
  return nil
}

Try / catch

out, err := exec.Command(cmd, args...).CombinedOutput()
if err != nil && strings.Contains(err.Error(), "failed to start command") {
  // inspect wrapped cause: ENOENT/EACCES/ENOEXEC
  log.Printf("task launch failed, command=%s: %v", cmd, err)
}

Prevention

When it happens

Trigger: runTTY is invoked via run() with command.Tty enabled, and processStart returns an error from cmd.Start — e.g. binary not found, exec format error, fork/exec permission denied, or resource limits.

Common situations: Typo in driver command path (task driver config 'command'), missing binary inside a chroot/task dir, non-executable file, incompatible architecture binary (exec format error), PID/file-descriptor limits exhausted.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/a134233bb32b0073. Report an issue: GitHub.