shadowsocks/shadowsocks-rust · error
plugin-mode must be one of `tcp_only` (default), `udp_only`
Error message
plugin-mode must be one of `tcp_only` (default), `udp_only` and `tcp_and_udp`
What it means
When --plugin is configured, the optional PLUGIN_MODE value is parsed into the Mode enum (TcpOnly/UdpOnly/TcpAndUdp); an unknown string makes .parse fail and the expect panics with this message. It restricts plugin-mode to the three documented values, defaulting to tcp_only when the flag is absent.
Source
Thrown at src/service/local.rs:675
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(),
plugin_mode: matches
.get_one::<String>("PLUGIN_MODE")
.map(|x| {
x.parse::<Mode>()
.expect("plugin-mode must be one of `tcp_only` (default), `udp_only` and `tcp_and_udp`")
})
.unwrap_or(Mode::TcpOnly),
};
sc.set_plugin(plugin);
}
config.server.push(ServerInstanceConfig::with_server_config(sc));
}
if let Some(mut svr_addr) = matches.get_one::<ServerConfig>("SERVER_URL").cloned() {
svr_addr.set_source(ServerSource::CommandLine);
config.server.push(ServerInstanceConfig::with_server_config(svr_addr));
}
#[cfg(feature = "local-flow-stat")]
{
use shadowsocks_service::config::LocalFlowStatAddress;View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Use exactly one of: tcp_only, udp_only, tcp_and_udp (lowercase, underscores).
- Omit --plugin-mode to get the tcp_only default.
- Check the Mode enum's FromStr for the accepted spellings of your binary version.
Example fix
// before sslocal --plugin v2ray-plugin --plugin-mode both ... // after sslocal --plugin v2ray-plugin --plugin-mode tcp_and_udp ...
Defensive patterns
Strategy: validation
Validate before calling
const VALID_MODES: [&str; 3] = ["tcp_only", "udp_only", "tcp_and_udp"];
if let Some(m) = matches.get_one::<String>("PLUGIN_MODE") {
if !VALID_MODES.contains(&m.as_str()) {
return Err(format!("invalid plugin-mode: {}", m));
}
} Try / catch
let mode = m.parse::<Mode>().unwrap_or_else(|_| {
eprintln!("plugin-mode must be tcp_only, udp_only or tcp_and_udp");
std::process::exit(2);
}); Prevention
- Only use the exact lowercase underscore forms: tcp_only, udp_only, tcp_and_udp.
- Omit --plugin-mode to accept the tcp_only default.
- Generate plugin flags from a checked template, not free text.
When it happens
Trigger: Running sslocal with --plugin and --plugin-mode set to something other than tcp_only, udp_only, or tcp_and_udp — e.g. "both", "tcp", "TCP_ONLY", or a mode supported only by ssserver.
Common situations: Case sensitivity mistakes; inventing mode names; copying server-side mode values that don't exist for the local plugin path; config generators emitting wrong keys.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/0bd40b0f4daeebe8.
Report an issue: GitHub.