{"record":{"id":"ea2dcf2a55470e2e","repo":"shadowsocks/shadowsocks-rust","slug":"tunnel-requires-forward-address","errorCode":null,"errorMessage":"tunnel requires forward address","messagePattern":"tunnel requires forward address","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/shadowsocks-service/src/local/mod.rs","lineNumber":332,"sourceCode":"                    if let Some(n) = local_config.launchd_tcp_socket_name {\n                        server_builder.set_launchd_tcp_socket_name(n);\n                    }\n                    #[cfg(target_os = \"macos\")]\n                    if let Some(n) = local_config.launchd_udp_socket_name {\n                        server_builder.set_launchd_udp_socket_name(n);\n                    }\n\n                    let server = server_builder.build().await?;\n                    local_server.socks_servers.push(server);\n                }\n                #[cfg(feature = \"local-tunnel\")]\n                ProtocolType::Tunnel => {\n                    let client_addr = match local_config.addr {\n                        Some(a) => a,\n                        None => return Err(io::Error::other(\"tunnel requires local address\")),\n                    };\n\n                    let forward_addr = local_config.forward_addr.expect(\"tunnel requires forward address\");\n\n                    let mut server_builder =\n                        TunnelBuilder::with_context(context.clone(), forward_addr.clone(), client_addr, balancer);\n\n                    if let Some(c) = config.udp_max_associations {\n                        server_builder.set_udp_capacity(c);\n                    }\n                    if let Some(d) = config.udp_timeout {\n                        server_builder.set_udp_expiry_duration(d);\n                    }\n                    server_builder.set_mode(local_config.mode);\n                    if let Some(udp_addr) = local_config.udp_addr {\n                        server_builder.set_udp_bind_addr(udp_addr);\n                    }\n\n                    #[cfg(target_os = \"macos\")]\n                    if let Some(n) = local_config.launchd_tcp_socket_name {\n                        server_builder.set_launchd_tcp_socket_name(n);","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/shadowsocks/shadowsocks-rust/blob/8eb0f0a65b1d976ab6bed5787327ef86529b0435/crates/shadowsocks-service/src/local/mod.rs#L314-L350","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add forward_addr to the tunnel local config, e.g. forward_addr = \"target.host:port\"","Double-check the config section actually uses protocol = \"tunnel\" only where forward_addr is defined","Prefer explicit configuration over defaults — validate config files before deployment","If tunnel mode is unintended, switch protocol back to socks/http and remove the tunnel assumptions"],"exampleFix":"# before\nclass = \"local\"\nprotocol = \"tunnel\"\nlocal_address = \"127.0.0.1:1080\"\n# after\nclass = \"local\"\nprotocol = \"tunnel\"\nlocal_address = \"127.0.0.1:1080\"\nforward_addr = \"example.com:80\"","handlingStrategy":"validation","validationCode":"// Validate tunnel config before constructing LocalServer\nif local_config.protocol == ProtocolType::Tunnel {\n    assert!(local_config.addr.is_some(), \"tunnel requires local address\");\n    assert!(local_config.forward_addr.is_some(), \"tunnel requires forward_addr\");\n}","typeGuard":"fn tunnel_config_ok(cfg: &LocalConfig) -> bool {\n    cfg.protocol != ProtocolType::Tunnel || (cfg.addr.is_some() && cfg.forward_addr.is_some())\n}","tryCatchPattern":"// This is a panic (expect), not an Err — guard by validating config first\nif !tunnel_config_ok(&config) {\n    eprintln!(\"invalid tunnel config: forward_addr is required\");\n    std::process::exit(2);\n}\nlet server = LocalServer::new(context, config)?;","preventionTips":["Always pair protocol = \"tunnel\" with a forward_addr in the config","Run shadowsocks' config validation (or a schema check) before launch","Keep tunnel configs in a separate template including all required fields","Remember this path panics via expect — never construct LocalServer with unvalidated user input"],"tags":["tunnel","configuration","missing-field","panic"],"backgroundTag":"missing-required-config-field","analyzedSha":"8eb0f0a65b1d976ab6bed5787327ef86529b0435","analyzedAt":"2026-09-09T12:20:43.168Z","contentChangedAt":"2026-09-09T12:20:43.168Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}