| Tested on | RHEL 10.2 with Go 1.27.0 |
|---|---|
| Package | go 1.27.0golang.org/x/term v0.45.0 |
| Applies to | Go 1.25+ |
| Privilege | Normal user |
| Scope | Hide password input with term.ReadPassword, term.IsTerminal, non-TTY handling, and the difference between no-echo and asterisk masking. Does not cover authentication design or credential storage. |
| Related guides | Golang os package Environment variables in Go Getting started with Go Golang context |
For a CLI that needs to accept a password without showing typed characters, term.ReadPassword from golang.org/x/term is the simplest approach. It reads one line from a terminal while disabling local echo, so the password is not displayed as the user types.
ReadPassword hides input completely. It does not automatically display ***** while typing.
Hiding input only affects terminal display; password hashing is a separate storage and verification concern.
Install golang.org/x/term
Pin the release tested in this article:
go get golang.org/x/term@v0.45.0go: downloading golang.org/x/term v0.45.0
go: downloading golang.org/x/sys v0.47.0
go: added golang.org/x/sys v0.47.0
go: added golang.org/x/term v0.45.0The term package depends on golang.org/x/sys for platform-specific terminal control. golang.org/x/term v0.45.0 requires Go 1.25 or newer.
Hide password input with term.ReadPassword
The signature is func ReadPassword(fd int) ([]byte, error). Pass int(os.Stdin.Fd()) so you read from the same stdin handle your process uses.
Print the prompt before calling ReadPassword. ReadPassword does not echo the Enter key, so print a newline after the call to move subsequent output to the next line. The returned slice does not include the newline character.
Save this program as main.go:
package main
import (
"fmt"
"os"
"golang.org/x/term"
)
func main() {
fd := int(os.Stdin.Fd())
fmt.Print("Password: ")
password, err := term.ReadPassword(fd)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println()
if len(password) == 0 {
fmt.Fprintln(os.Stderr, "empty password")
os.Exit(1)
}
fmt.Println("Password received")
}Run it in a real terminal with go run main.go (not an IDE run pane without a TTY). Sample interaction:
Password:
Password receivedNothing appears as you type at the Password: prompt; after Enter you get a blank line and Password received. Check err before using the returned []byte.
Detect non-terminal input with term.IsTerminal
ReadPassword already attempts terminal-specific handling and returns an error when the descriptor cannot be used appropriately. term.IsTerminal(fd) independently reports whether a file descriptor is a terminal.
If your program needs different behavior for interactive and non-interactive input, check stdin with term.IsTerminal before calling ReadPassword:
fd := int(os.Stdin.Fd())
fmt.Println(term.IsTerminal(fd))When this prints false, stdin is often a pipe, a redirected file, output from another process, or a CI runner without a TTY. ReadPassword is for terminal input; pipes and redirected stdin need a separate input path.
One common pattern is to fail early with a clear message:
if !term.IsTerminal(fd) {
fmt.Fprintln(os.Stderr, "password input requires an interactive terminal")
os.Exit(1)
}Add that guard to main.go before the ReadPassword call. Piped stdin then stops before ReadPassword runs:
echo test | go run main.gopassword input requires an interactive terminal
exit status 1Does ReadPassword show asterisks?
term.ReadPassword disables echo completely; it does not print *. Asterisk masking requires reading individual keystrokes in raw mode or using a terminal/TUI library. For normal CLI password prompts, no-echo input with ReadPassword is simpler.
Common errors
| Problem | Cause |
|---|---|
inappropriate ioctl for device on Linux/Unix |
stdin is not an interactive terminal |
| Password appears on screen | Input was read with normal stdin APIs instead of terminal echo control |
No ***** appears |
ReadPassword disables echo; it does not mask with asterisks |
| Works on terminal but not pipe/CI | stdin is not a terminal; use a separate non-interactive input path |
| Extra blank or missing line after prompt | Handle prompt and newline placement explicitly |
Summary
Hide password input in Go with term.ReadPassword(int(os.Stdin.Fd())): print a prompt, read without echo, then print a newline before any further output. The function does not draw asterisks while typing.
When stdin may not be a terminal, use term.IsTerminal to branch to a separate non-interactive input path. Do not print or log the returned bytes in examples or debug output.
References
- golang.org/x/term —
ReadPasswordandIsTerminal - os package —
StdinandFd()

