{"record":{"id":"83e0c30aedb15c52","repo":"restic/restic","slug":"unable-to-read-password-w","errorCode":null,"errorMessage":"unable to read password: %w","messagePattern":"unable to read password: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/global/global.go","lineNumber":240,"sourceCode":"\n// readPassword reads the password from a password file, the environment\n// variable RESTIC_PASSWORD or prompts the user. If the context is canceled,\n// the function leaks the password reading goroutine.\nfunc readPassword(ctx context.Context, gopts Options, prompt string) (string, error) {\n\tif gopts.InsecureNoPassword {\n\t\tif gopts.Password != \"\" {\n\t\t\treturn \"\", errors.Fatal(\"--insecure-no-password must not be specified together with providing a password via a cli option or environment variable\")\n\t\t}\n\t\treturn \"\", nil\n\t}\n\n\tif gopts.Password != \"\" {\n\t\treturn gopts.Password, nil\n\t}\n\n\tpassword, err := gopts.Term.ReadPassword(ctx, prompt)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to read password: %w\", err)\n\t}\n\n\tif len(password) == 0 {\n\t\treturn \"\", errors.Fatal(\"an empty password is not allowed by default. Pass the flag `--insecure-no-password` to restic to disable this check\")\n\t}\n\n\treturn password, nil\n}\n\n// ReadPasswordTwice calls ReadPassword two times and returns an error when the\n// passwords don't match. If the context is canceled, the function leaks the\n// password reading goroutine.\nfunc ReadPasswordTwice(ctx context.Context, gopts Options, prompt1, prompt2 string) (string, error) {\n\tpw1, err := readPassword(ctx, gopts, prompt1)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif gopts.Term.InputIsTerminal() {","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/restic/restic/blob/a80be1478a4c537f8396e0db2b05120aa78f11e0/internal/global/global.go#L222-L258","documentation":"When no password was supplied via the CLI option, environment variables, or a password file, restic prompts on the terminal (gopts.Term.ReadPassword). This error wraps a failure of that terminal read itself - no usable TTY, EOF on input, or a canceled context - and is unrelated to whether the password is correct.","triggerScenarios":"Running restic where stdin/stdout is not a terminal: cron jobs, systemd services, docker without -t, CI pipelines, ssh without TTY allocation; the surrounding context being canceled while the prompt is open; a closed or permission-denied /dev/tty; piped input ending before the password is entered.","commonSituations":"Automated nightly backups without RESTIC_PASSWORD_FILE configured; restic inside Docker or Kubernetes CronJobs; scripts that redirect stdin and still expect interactive prompting to work.","solutions":["Set RESTIC_PASSWORD_FILE (file mode 600) or pass --password-file for every non-interactive run","Use RESTIC_PASSWORD_COMMAND with a secret manager when storing a file is not an option","Provide a real terminal for interactive use (docker run -t, ssh -tt)","Check that the job's context is not canceled while credentials are being read"],"exampleFix":"# before (cron job, no TTY)\nrestic -r s3:s3.amazonaws.com/bucket backup /data  # fails: unable to read password\n\n# after\nprintf 's3cret' > /etc/restic/pass && chmod 600 /etc/restic/pass\nRESTIC_PASSWORD_FILE=/etc/restic/pass restic -r s3:s3.amazonaws.com/bucket backup /data","handlingStrategy":"validation","validationCode":"// decide before prompting whether a password source exists\nif gopts.Password == \"\" &&\n    os.Getenv(\"RESTIC_PASSWORD_FILE\") == \"\" &&\n    os.Getenv(\"RESTIC_PASSWORD_COMMAND\") == \"\" &&\n    !isTerminal(os.Stdin) {\n    return errors.New(\"no TTY available; provide --password-file or RESTIC_PASSWORD_FILE\")\n}","typeGuard":"func isTerminal(f *os.File) bool {\n    fi, err := f.Stat()\n    return err == nil && fi.Mode()&os.ModeCharDevice != 0\n}","tryCatchPattern":"password, err := resolvePassword(ctx, gopts, \"enter password\")\nif err != nil {\n    if errors.Is(err, context.Canceled) {\n        return err\n    }\n    return fmt.Errorf(\"non-interactive run without a password source: %w\", err)\n}","preventionTips":["Configure RESTIC_PASSWORD_FILE with mode 600 for every automated run","Never rely on interactive prompts in cron, systemd, or containers","Use RESTIC_PASSWORD_COMMAND with a secret manager for short-lived credentials","Fail fast in scripts by testing credentials with a cheap command (restic snapshots) first"],"tags":["password","terminal","authentication","automation","tty"],"backgroundTag":null,"analyzedSha":"a80be1478a4c537f8396e0db2b05120aa78f11e0","analyzedAt":"2026-08-15T15:30:29.928Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}