hashicorp/nomad · error
failed to open a tty: %v
Error message
failed to open a tty: %v
What it means
The shared executor's execHelper.runTTY allocates a pseudo-terminal (pty) for an interactive exec session via newTerminal(). If opening the tty fails, the error is wrapped as 'failed to open a tty' and the exec session aborts.
Source
Thrown at drivers/shared/executor/exec_utils.go:51
// processStart starts the process, like `exec.Cmd.Start()`
processStart func() error
// processWait blocks until command terminates and returns its final state
processWait func() (*os.ProcessState, error)
}
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)
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Retry without a TTY (non-interactive exec) if interactivity is not required
- Check /dev/pts is mounted and ptys available inside the container (mount devpts, raise max)
- Verify container/devices config allows /dev/ptmx; relax seccomp if it blocks openpty
- Check kernel limits (file descriptors, kernel.pty.max) on the host
Example fix
// before
exec, err := client.AllocExec(ctx, opts) // opts.Tty = true
// after
if ttyUnavailable { opts.Tty = false } // fall back to non-tty exec
exec, err := client.AllocExec(ctx, opts) Defensive patterns
Strategy: fallback
Validate before calling
// check container has pty support before requesting tty exec
if _, err := os.Stat("/dev/ptmx"); err != nil {
opts.Tty = false // fall back to non-tty
} Try / catch
if err := execSession(); err != nil {
if strings.Contains(err.Error(), "failed to open a tty") {
opts.Tty = false
err = execSession() // retry without tty
}
} Prevention
- Ensure /dev/pts is mounted in containers used for exec
- Avoid requesting TTY for non-interactive automation
- Monitor pty limits (kernel.pty.max) on busy hosts
When it happens
Trigger: run() selects runTTY when a TTY is requested and newTerminal() (pty open, typically /dev/ptmx) fails — e.g. no free ptys, /dev/ptmx missing, or permission denied inside the container/namespace.
Common situations: Alloc exec with -t / interactive terminal into a container lacking /dev/pts; exhausted pty limits (devpts max); containers started without a device tree; restricted seccomp blocking pty allocation.
Related errors
- failed to set command tty: %v
- not a terminal
- no exec command is configured
- unknown task name %q
- task %q not started yet.
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/e52c086a27c8388a.
Report an issue: GitHub.