shadowsocks/shadowsocks-rust · error

missing `forward_addr` in configuration

Error message

missing `forward_addr` in configuration

What it means

During Config::validate() for a local server with ProtocolType::Tunnel (the port-forwarding/tunnel local), the `forward_addr` field must be set. If it is None, the config is rejected with ErrorKind::MissingField. A tunnel without a forward address has nowhere to relay traffic, so the library refuses to start it.

Source

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

            }
        }

        match self.protocol {
            #[cfg(feature = "local-dns")]
            ProtocolType::Dns => {
                if self.local_dns_addr.is_none() || self.remote_dns_addr.is_none() {
                    let err = Error::new(
                        ErrorKind::MissingField,
                        "missing `local_dns_addr` or `remote_dns_addr` in configuration",
                        None,
                    );
                    return Err(err);
                }
            }
            #[cfg(feature = "local-tunnel")]
            ProtocolType::Tunnel => {
                if self.forward_addr.is_none() {
                    let err = Error::new(ErrorKind::MissingField, "missing `forward_addr` in configuration", None);
                    return Err(err);
                }
            }

            #[cfg(feature = "local-http")]
            ProtocolType::Http => {
                if !self.mode.enable_tcp() {
                    let err = Error::new(ErrorKind::Invalid, "TCP mode have to be enabled for http", None);
                    return Err(err);
                }
            }

            _ => {}
        }

        Ok(())
    }

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Add `forward_addr` to the tunnel configuration (e.g. "forward_addr": "example.com:443").
  2. In code, set config.forward_addr = Some(...) before validating.
  3. If tunneling is not intended, switch the local protocol to socks/http/dns as appropriate.

Example fix

// before
{ "protocol": "tunnel", "local_addr": "0.0.0.0:8388" }
// after
{ "protocol": "tunnel", "local_addr": "0.0.0.0:8388", "forward_addr": "example.com:443" }
Defensive patterns

Strategy: validation

Validate before calling

if cfg.protocol == ProtocolType::Tunnel && cfg.forward_addr.is_none() {
    return Err("tunnel protocol requires `forward_addr`");
}

Type guard

fn tunnel_addr_set(cfg: &Config) -> bool {
    cfg.protocol != ProtocolType::Tunnel || cfg.forward_addr.is_some()
}

Try / catch

match Config::load(path) {
    Ok(cfg) => start(cfg),
    Err(e) if e.to_string().contains("forward_addr") => eprintln!("tunnel config must set forward_addr"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling Config::validate() (directly or via Config::load) when config_type is Local, protocol is ProtocolType::Tunnel, and forward_addr is None.

Common situations: A JSON config with "protocol": "tunnel" but no "forward_addr" key; programmatically built configs missing the forward_addr setter; copying a tunnel config from an example and deleting the destination address.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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