{"record":{"id":"779e6f91de354a8a","repo":"slackhq/nebula","slug":"errnoterminal","errorCode":"ErrNoTerminal","errorMessage":"cannot read password from nonexistent terminal","messagePattern":"cannot read password from nonexistent terminal","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/nebula-cert/passwords.go","lineNumber":11,"sourceCode":"package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"golang.org/x/term\"\n)\n\nvar ErrNoTerminal = errors.New(\"cannot read password from nonexistent terminal\")\n\ntype PasswordReader interface {\n\tReadPassword() ([]byte, error)\n}\n\ntype StdinPasswordReader struct{}\n\nfunc (pr StdinPasswordReader) ReadPassword() ([]byte, error) {\n\tif !term.IsTerminal(int(os.Stdin.Fd())) {\n\t\treturn nil, ErrNoTerminal\n\t}\n\n\tpassword, err := term.ReadPassword(int(os.Stdin.Fd()))\n\t// Terminal echo is off while reading, so the user's Enter key does not\n\t// produce a visible newline. Emit one on stderr to match the prompt.\n\tfmt.Fprintln(os.Stderr)\n\n\treturn password, err","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cmd/nebula-cert/passwords.go#L1-L29","documentation":"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.","triggerScenarios":"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'.","commonSituations":"Automated jobs invoking nebula-cert expecting to prompt for a passphrase; containerized runs lacking a pseudo-TTY; piping echo 'secret' | into the tool.","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."],"exampleFix":"// before: prompts, fails in CI\n$ nebula-cert ca -name bastion\n\n// after: supply passphrase non-interactively\n$ nebula-cert ca -name bastion -passphrase \"$(cat /run/secrets/ca_pass)\"","handlingStrategy":"type-guard","validationCode":"if !term.IsTerminal(int(os.Stdin.Fd())) {\n    // must supply passphrase via flag/file; interactive prompt impossible\n    return errors.New(\"stdin is not a TTY; provide -passphrase or -passphrase-file\")\n}","typeGuard":"func canPromptInteractively(f *os.File) bool {\n    return term.IsTerminal(int(f.Fd()))\n}","tryCatchPattern":"passphrase, err := pr.ReadPassword()\nif errors.Is(err, ErrNoTerminal) {\n    return fmt.Errorf(\"cannot prompt for passphrase without a TTY; use -passphrase-file\")\n} else if err != nil {\n    return fmt.Errorf(\"failed reading password: %w\", err)\n}","preventionTips":["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."],"tags":["terminal","password","stdin","automation"],"backgroundTag":"no-tty-stdin","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}