shadowsocks/shadowsocks-rust · error

tunnel requires forward address

Error message

tunnel requires forward address

What it means

The local server's Tunnel protocol mode requires a forward_addr in the LocalConfig: the destination to which all tunneled traffic is forwarded. The code expects it unconditionally (Option::expect) when building TunnelBuilder, so a Tunnel-mode config without forward_addr panics with "tunnel requires forward address". Unlike some other errors this is a panic, not an io::Error return.

Source

Thrown at crates/shadowsocks-service/src/local/mod.rs:332

                    if let Some(n) = local_config.launchd_tcp_socket_name {
                        server_builder.set_launchd_tcp_socket_name(n);
                    }
                    #[cfg(target_os = "macos")]
                    if let Some(n) = local_config.launchd_udp_socket_name {
                        server_builder.set_launchd_udp_socket_name(n);
                    }

                    let server = server_builder.build().await?;
                    local_server.socks_servers.push(server);
                }
                #[cfg(feature = "local-tunnel")]
                ProtocolType::Tunnel => {
                    let client_addr = match local_config.addr {
                        Some(a) => a,
                        None => return Err(io::Error::other("tunnel requires local address")),
                    };

                    let forward_addr = local_config.forward_addr.expect("tunnel requires forward address");

                    let mut server_builder =
                        TunnelBuilder::with_context(context.clone(), forward_addr.clone(), client_addr, balancer);

                    if let Some(c) = config.udp_max_associations {
                        server_builder.set_udp_capacity(c);
                    }
                    if let Some(d) = config.udp_timeout {
                        server_builder.set_udp_expiry_duration(d);
                    }
                    server_builder.set_mode(local_config.mode);
                    if let Some(udp_addr) = local_config.udp_addr {
                        server_builder.set_udp_bind_addr(udp_addr);
                    }

                    #[cfg(target_os = "macos")]
                    if let Some(n) = local_config.launchd_tcp_socket_name {
                        server_builder.set_launchd_tcp_socket_name(n);

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Add forward_addr to the tunnel local config, e.g. forward_addr = "target.host:port"
  2. Double-check the config section actually uses protocol = "tunnel" only where forward_addr is defined
  3. Prefer explicit configuration over defaults — validate config files before deployment
  4. If tunnel mode is unintended, switch protocol back to socks/http and remove the tunnel assumptions

Example fix

# before
class = "local"
protocol = "tunnel"
local_address = "127.0.0.1:1080"
# after
class = "local"
protocol = "tunnel"
local_address = "127.0.0.1:1080"
forward_addr = "example.com:80"
Defensive patterns

Strategy: validation

Validate before calling

// Validate tunnel config before constructing LocalServer
if local_config.protocol == ProtocolType::Tunnel {
    assert!(local_config.addr.is_some(), "tunnel requires local address");
    assert!(local_config.forward_addr.is_some(), "tunnel requires forward_addr");
}

Type guard

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

Try / catch

// This is a panic (expect), not an Err — guard by validating config first
if !tunnel_config_ok(&config) {
    eprintln!("invalid tunnel config: forward_addr is required");
    std::process::exit(2);
}
let server = LocalServer::new(context, config)?;

Prevention

When it happens

Trigger: Configuring protocol = "tunnel" with a local listening address but omitting forward_addr (e.g. missing `forward_addr = "example.com:80"` in the [local] section), then constructing LocalServer::new.

Common situations: Copy-pasting a SOCKS/HTTP local config and switching protocol to tunnel without adding forward_addr; older config files predating the tunnel option; tooling generating configs that skip tunnel-specific fields.

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/ea2dcf2a55470e2e. Report an issue: GitHub.