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
This panic comes from an `.expect()` on `str::parse::<Mode>()` while building the plugin configuration from the `--plugin-mode` CLI flag. The string is parsed into the shadowsocks `Mode` enum, which only accepts `tcp_only`, `udp_only`, and `tcp_and_udp`; any other value panics with this message instead of returning a graceful config error.
Source
Thrown at src/service/server.rs:377
Ok(sc) => sc,
Err(err) => {
panic!("failed to create ServerConfig, error: {}", err);
}
};
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);
}
// For historical reason, servers that are created from command-line have to be tcp_only.
sc.set_mode(Mode::TcpOnly);
if matches.get_flag("UDP_ONLY") {
sc.set_mode(Mode::UdpOnly);
}
if matches.get_flag("TCP_AND_UDP") {
sc.set_mode(Mode::TcpAndUdp);
}
View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Set --plugin-mode to exactly one of `tcp_only`, `udp_only`, or `tcp_and_udp` (lowercase, underscores).
- Omit --plugin-mode entirely to get the default `tcp_only`.
- If the value comes from a script/config file, validate it against the three allowed strings before launching.
- Wrap the parse in a checked path (e.g. `Mode::from_str` with error handling) instead of `.expect` if patching the code.
Example fix
// before sslocal --plugin obfs --plugin-opts "obfs=http" --plugin-mode TCP_ONLY // after sslocal --plugin obfs --plugin-opts "obfs=http" --plugin-mode tcp_only
Defensive patterns
Strategy: validation
Validate before calling
const VALID_MODES: [&str; 3] = ["tcp_only", "udp_only", "tcp_and_udp"];
if !VALID_MODES.contains(&plugin_mode.as_str()) {
panic!("plugin-mode must be one of tcp_only, udp_only, tcp_and_udp, got: {plugin_mode}");
} Type guard
fn is_valid_mode(s: &str) -> bool {
matches!(s, "tcp_only" | "udp_only" | "tcp_and_udp")
} Prevention
- Keep --plugin-mode values lowercase with underscores exactly as documented.
- Store launch commands in scripts and validate flags with shellcheck or a lint pass.
- Omit the flag when the default (tcp_only) is desired.
When it happens
Trigger: Passing `--plugin-mode` (PLUGIN_OPT/PLUGIN_MODE matches in src/service/server.rs:377) with a value that is not exactly `tcp_only`, `udp_only`, or `tcp_and_udp`, e.g. `tcp`, `TCP_ONLY`, `udp`, or a typo like `tcp-and-udp`.
Common situations: Users copying plugin flags from shadowsocks-libev or other implementations that use different mode spellings (`tcp_and_udp` vs `TCP`), typos in launch scripts or systemd units, case mistakes since parsing is case-sensitive.
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
- `password` is required for server {svr_addr}
- failed to create ServerConfig, error: {}
- dns
- all plugins are exited. all connections may fail, check your
- unsupported syslog facility: {}
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/d1c499d14572a62c.
Report an issue: GitHub.