slackhq/nebula · error
ErrNoTerminal
ErrNoTerminal
Error message
cannot read password from nonexistent terminal
What it means
ErrNoTerminal is defined in cmd/nebula-cert/passwords.go and returned by StdinPasswordReader.ReadPassword when stdin is not a terminal (term.IsTerminal is false), so an interactive passphrase cannot be prompted. Functions that require interactive encryption (ca, signCert) propagate it to distinguish 'no TTY available' from other password errors.
Source
Thrown at cmd/nebula-cert/passwords.go:11
package main
import (
"errors"
"fmt"
"os"
"golang.org/x/term"
)
var ErrNoTerminal = errors.New("cannot read password from nonexistent terminal")
type PasswordReader interface {
ReadPassword() ([]byte, error)
}
type StdinPasswordReader struct{}
func (pr StdinPasswordReader) ReadPassword() ([]byte, error) {
if !term.IsTerminal(int(os.Stdin.Fd())) {
return nil, ErrNoTerminal
}
password, err := term.ReadPassword(int(os.Stdin.Fd()))
// Terminal echo is off while reading, so the user's Enter key does not
// produce a visible newline. Emit one on stderr to match the prompt.
fmt.Fprintln(os.Stderr)
return password, errView on GitHub (pinned to dd8f660c0a)
Solutions
- Provide the passphrase non-interactively via the tool's passphrase flag/file (e.g. -passphrase or -passphrase-file) so no terminal is needed.
- Run inside a real TTY (docker run -it, ssh -t, `script -qc`).
- Inject a custom PasswordReader implementation in tests/automation instead of StdinPasswordReader.
- If encryption of the output key is not required, disable key encryption so no prompt is attempted.
Example fix
// before: prompts, fails in CI $ nebula-cert ca -name bastion // after: supply passphrase non-interactively $ nebula-cert ca -name bastion -passphrase "$(cat /run/secrets/ca_pass)"
Defensive patterns
Strategy: type-guard
Validate before calling
if !term.IsTerminal(int(os.Stdin.Fd())) {
// must supply passphrase via flag/file; interactive prompt impossible
return errors.New("stdin is not a TTY; provide -passphrase or -passphrase-file")
} Type guard
func canPromptInteractively(f *os.File) bool {
return term.IsTerminal(int(f.Fd()))
} Try / catch
passphrase, err := pr.ReadPassword()
if errors.Is(err, ErrNoTerminal) {
return fmt.Errorf("cannot prompt for passphrase without a TTY; use -passphrase-file")
} else if err != nil {
return fmt.Errorf("failed reading password: %w", err)
} Prevention
- Always provide the passphrase via flag/file in cron, systemd, CI, and Docker.
- Request a TTY when interactive prompting is needed (docker run -it, ssh -t).
- Inject a scripted PasswordReader in automated tests instead of StdinPasswordReader.
- Detect non-TTY stdin at script start and branch to non-interactive mode.
When it happens
Trigger: ReadPassword called on a non-TTY stdin, e.g. running 'nebula-cert ca ...' without a passphrase flag under cron/systemd/CI, in Docker without -t, or with stdin redirected from a file/pipe (passwords.go:21). ca.go:228 converts it to 'out-key must be encrypted interactively'.
Common situations: Automated jobs invoking nebula-cert expecting to prompt for a passphrase; containerized runs lacking a pseudo-TTY; piping echo 'secret' | into the tool.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/779e6f91de354a8a.
Report an issue: GitHub.