shadowsocks/shadowsocks-rust · error

server-addr

Error message

server-addr

What it means

The per-server SERVER_ADDR value is parsed into a ServerAddr via FromStr; an unparseable address makes .expect("server-addr") panic. ServerAddr accepts host:port or domain:port (optionally with a URI-ish form), so the string must resolve to a valid target server.

Source

Thrown at src/service/local.rs:652

                .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);
                }
            };
            sc.set_source(ServerSource::CommandLine);
            if let Some(timeout) = timeout {
                sc.set_timeout(timeout);
            }

            if let Some(p) = matches.get_one::<String>("PLUGIN").cloned() {
                let plugin = PluginConfig {
                    plugin: p,
                    plugin_opts: matches.get_one::<String>("PLUGIN_OPT").cloned(),
                    plugin_args: Vec::new(),

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Format the address as host:port, e.g. example.com:8388 or 1.2.3.4:8388.
  2. Wrap IPv6 literals in brackets: [2001:db8::1]:8388.
  3. Validate/trim the value in your launcher script before passing it.

Example fix

// before
sslocal -b 127.0.0.1:1080 -s "example.com" ...

// after
sslocal -b 127.0.0.1:1080 -s "example.com:8388" ...
Defensive patterns

Strategy: validation

Validate before calling

fn validate_server_addr(s: &str) -> Result<(), String> {
    s.parse::<shadowsocks::config::ServerAddr>()
        .map(|_| ())
        .map_err(|_| format!("invalid server-addr '{}': expected host:port", s))
}

Try / catch

let addr = s.parse::<ServerAddr>().unwrap_or_else(|_| {
    eprintln!("invalid server-addr: {}", s);
    std::process::exit(2);
});

Prevention

When it happens

Trigger: Passing a malformed SERVER_ADDR to sslocal, e.g. missing port (`example.com`), non-numeric port (`example.com:https`), empty string, or bracket mishandling for IPv6 (`::1:8388` instead of `[::1]:8388`).

Common situations: Hand-edited scripts; IPv6 addresses without brackets; whitespace or quotes captured into the argument; SIP002 URLs pasted into the wrong flag.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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