hashicorp/nomad · error

failed to get pty: %v

Error message

failed to get pty: %v

What it means

runTTY fails to obtain a pseudo-terminal via the ptyF factory function and wraps the error with this message. The pty allocation happens after the process starts when TTY mode is enabled; without a pty the stdin/stdout bridge cannot be established.

Source

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

	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()

	// force close streams to close out the stream copying goroutines
	tty.Close()

	// wait until we get all process output
	wg.Wait()

	// wait to flush out output
	stream.Send(cmdExitResult(ps, err))

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check host pty exhaustion: cat /proc/sys/kernel/pty/nr vs kernel.pty.max; increase or free ptys
  2. Ensure /dev/ptmx exists and /dev/pts is mounted in the execution environment
  3. Inspect the wrapped %v error for the underlying syscall failure
  4. Review security/seccomp/apparmor policies that may block pty ioctls
  5. If ptys are unnecessary, run the task with TTY disabled so runNoTTY is used

Example fix

// before (container without pts mounted)
// run: mount devpts failed
// after (host shell)
// sudo mount -t devpts devpts /dev/pts && sysctl -w kernel.pty.max=4096
Defensive patterns

Strategy: try-catch

Validate before calling

func ptysAvailable() bool {
  if _, err := os.Stat("/dev/ptmx"); err != nil { return false }
  b, err := os.ReadFile("/proc/sys/kernel/pty/nr")
  if err != nil { return false }
  max, err := os.ReadFile("/proc/sys/kernel/pty/max")
  if err != nil { return false }
  return strings.TrimSpace(string(b)) != strings.TrimSpace(string(max))
}

Try / catch

if err := startTaskWithTTY(); err != nil {
  if strings.Contains(err.Error(), "failed to get pty") {
    // fall back to non-tty launch or reschedule on another node
    return startTaskWithoutTTY()
  }
  return err
}

Prevention

When it happens

Trigger: run() with command.Tty=true reaches `pty, err := ptyF()` and pty allocation fails — typically openpty/ptmx open failures.

Common situations: Host out of ptys (kernel.pty.max exhausted), running inside a container/namespace without /dev/ptmx or restricted device cgroup, missing pty kernel module, seccomp/apparmor blocking TIOCGPTN/ioctl calls.

Related errors


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