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.
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.
Appendix: 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)