shadowsocks/shadowsocks-rust · error

invalid outbound_proxy

Error message

invalid outbound_proxy

What it means

shadowsocks-service validates each server's optional outbound_proxy when loading a service config. The configured proxy is converted into concrete proxy entries via into_proxies(); if that conversion fails (e.g. an unparsable proxy URL or unsupported scheme), the loader aborts with ErrorKind::Invalid and the message 'invalid outbound_proxy', attaching the underlying cause.

Solutions

  1. Check the nested cause (e) in the error for the exact parse failure
  2. Fix the proxy URL syntax in the server's outbound_proxy config field
  3. Use a supported scheme (socks5, http) for outbound_proxy
  4. Remove outbound_proxy from the server config if no outbound proxy is needed

Example fix

// before
"outbound_proxy": "socks5//127.0.0.1:1080"
// after
"outbound_proxy": "socks5://127.0.0.1:1080"
Defensive patterns

Strategy: validation

Validate before calling

fn validate_outbound_proxy(p: &str) -> Result<(), String> {
    let (scheme, rest) = p.split_once("://")
        .ok_or_else(|| "proxy URL missing '://'".to_string())?;
    match scheme {
        "socks5" | "http" => (),
        other => return Err(format!("unsupported proxy scheme: {other}")),
    }
    if rest.is_empty() { return Err("proxy URL has empty host".into()); }
    Ok(())
}

Type guard

fn is_supported_proxy_scheme(p: &str) -> bool {
    matches!(p.split_once("://"), Some(("socks5" | "http", _)))
}

Try / catch

match server_cfg.outbound_proxy {
    Some(p) => match p.into_proxies() {
        Ok(proxies) => instance.outbound_proxy = proxies,
        Err(e) => eprintln!("bad outbound_proxy: {e}"),
    },
    None => {}
}

Prevention

When it happens

Trigger: Parsing a server config (JSON or builder) where svr.outbound_proxy is Some, and into_proxies() fails — typically a malformed proxy URL string or a proxy scheme the library does not support.

Common situations: Typo in a socks5/http proxy URL in the JSON config; using a scheme like https:// or vmess:// that shadowsocks outbound proxy does not support; leaving an empty or truncated proxy string after editing the config by hand.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at crates/shadowsocks-service/src/config.rs:2441

                    server_instance.outbound_bind_addr = Some(outbound_bind_addr);
                }

                if let Some(ref outbound_bind_interface) = svr.outbound_bind_interface {
                    server_instance.outbound_bind_interface = Some(outbound_bind_interface.clone());
                }

                if let Some(outbound_udp_allow_fragmentation) = svr.outbound_udp_allow_fragmentation {
                    server_instance.outbound_udp_allow_fragmentation = Some(outbound_udp_allow_fragmentation);
                }

                if let Some(inbound_udp_allow_fragmentation) = svr.inbound_udp_allow_fragmentation {
                    server_instance.inbound_udp_allow_fragmentation = Some(inbound_udp_allow_fragmentation);
                }

                if let Some(proxy_config) = svr.outbound_proxy {
                    server_instance.outbound_proxy = proxy_config
                        .into_proxies()
                        .map_err(|e| Error::new(ErrorKind::Invalid, "invalid outbound_proxy", Some(e)))?;
                }

                nconfig.server.push(server_instance);
            }
        }

        // Set timeout globally
        if let Some(timeout) = config.timeout {
            let timeout = Duration::from_secs(timeout);
            // Set as a default timeout
            for inst in &mut nconfig.server {
                let svr = &mut inst.config;
                if svr.timeout().is_none() {
                    svr.set_timeout(timeout);
                }
            }
        }

View on GitHub (pinned to 8eb0f0a65b)