shadowsocks/shadowsocks-rust · error

`password` is required for server {svr_addr}

Error message

`password` is required for server {svr_addr}

What it means

When starting ssserver from CLI flags, if `--password` is not supplied and the chosen encryption method requires a key (anything other than none/plain), the code falls back to reading the password interactively / from the environment via read_server_password; if that also fails, it panics. Encryption methods that derive a key cannot run without a shared password.

Source

Thrown at src/service/server.rs:349

        };

        if let Some(svr_addr) = matches.get_one::<String>("SERVER_ADDR") {
            let method = matches
                .get_one::<String>("ENCRYPT_METHOD")
                .map(|x| x.parse::<CipherKind>().expect("method"))
                .expect("`method` is required");

            let password = match matches.get_one::<String>("PASSWORD") {
                Some(pwd) => read_variable_field_value(pwd).into(),
                None => {
                    // NOTE: svr_addr should have been checked by crate::vparser
                    if method.is_none() {
                        // If method doesn't need a key (none, plain), then we can leave it empty
                        String::new()
                    } else {
                        match crate::password::read_server_password(svr_addr) {
                            Ok(pwd) => pwd,
                            Err(..) => panic!("`password` is required for server {svr_addr}"),
                        }
                    }
                }
            };

            let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
            let timeout = matches.get_one::<u64>("TIMEOUT").map(|x| Duration::from_secs(*x));

            let mut sc = match ServerConfig::new(svr_addr, password, method) {
                Ok(sc) => sc,
                Err(err) => {
                    panic!("failed to create ServerConfig, error: {}", err);
                }
            };
            if let Some(timeout) = timeout {
                sc.set_timeout(timeout);
            }

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Pass the password explicitly: `ssserver -s 0.0.0.0:8388 -m aes-256-gcm --password '...'`
  2. Use environment-variable password syntax supported by the binary (e.g. --password '$SS_PASSWORD') in non-interactive environments
  3. Run ssserver interactively once so the password prompt can be answered and stored
  4. If no encryption is intended, use `-m none` so an empty password is allowed

Example fix

// before
ssserver -s 0.0.0.0:8388 -m aes-256-gcm
// after
ssserver -s 0.0.0.0:8388 -m aes-256-gcm --password '$SS_SERVER_PASSWORD'
Defensive patterns

Strategy: validation

Validate before calling

if !args.contains(&"--password") && method != "none" {
    if std::env::var("SS_PASSWORD").is_err() && std::io::stdin().is_terminal() == false {
        eprintln!("refusing to start: password required for method {method} in non-interactive mode");
        std::process::exit(2);
    }
}

Try / catch

// panic is not catchable in stable Rust; when spawning check output:
let out = Command::new("ssserver").args(&args).output()?;
if !out.status.success() && String::from_utf8_lossy(&out.stderr).contains("`password` is required") { /* surface credential error */ }

Prevention

When it happens

Trigger: Running `ssserver -s 0.0.0.0:8388 -m aes-256-gcm` without `--password` and without SS_PASSWORD-derived interactive/env input available (non-interactive stdin, no TTY); `--password` pointing at a variable field that fails to resolve.

Common situations: Running ssserver under systemd/docker where stdin is not a TTY so the interactive password prompt can't be answered; CI scripts that forgot the password flag; method changed from `none` to an AEAD cipher in a script that never set a password.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/9154da2cb1ed1126. Report an issue: GitHub.