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
The manager service parses `PLUGIN_MODE` into the `Mode` enum (`tcp_only` / `udp_only` / `tcp_and_udp`) and panics with this message when the string doesn't match any variant. This value controls whether the SIP003 plugin handles TCP only, UDP only, or both.
Source
Thrown at src/service/manager.rs:387
if let Some(t) = matches.get_one::<u64>("TIMEOUT") {
manager_config.timeout = Some(Duration::from_secs(*t));
}
if let Some(sh) = matches.get_one::<ManagerServerHost>("SERVER_HOST").cloned() {
manager_config.server_host = sh;
}
if let Some(p) = matches.get_one::<String>("PLUGIN").cloned() {
manager_config.plugin = Some(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),
});
}
#[cfg(unix)]
if let Some(server_mode) = matches.get_one::<ManagerServerMode>("MANAGER_SERVER_MODE").cloned() {
manager_config.server_mode = server_mode;
}
#[cfg(unix)]
if let Some(server_working_directory) =
matches.get_one::<PathBuf>("MANAGER_SERVER_WORKING_DIRECTORY").cloned()
{
manager_config.server_working_directory = server_working_directory;
}
}
View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Use exactly one of: `tcp_only`, `udp_only`, `tcp_and_udp` (lowercase, underscores)
- Omit `--plugin-mode` entirely to get the default `tcp_only`
- Check the config file for a `plugin_mode` value and normalize it to a valid variant
Example fix
// before --plugin-mode "TCP-AND-UDP" // after --plugin-mode "tcp_and_udp"
Defensive patterns
Strategy: validation
Validate before calling
const MODES: &[&str] = &["tcp_only", "udp_only", "tcp_and_udp"];
if let Some(m) = plugin_mode.as_deref() {
assert!(MODES.contains(&m), "plugin-mode must be one of {MODES:?}");
} Type guard
fn is_valid_plugin_mode(s: &str) -> bool {
matches!(s, "tcp_only" | "udp_only" | "tcp_and_udp")
} Try / catch
// use parse and report instead of expect
let mode: Mode = s.parse().map_err(|_| anyhow!("plugin-mode must be tcp_only|udp_only|tcp_and_udp"))?; Prevention
- Use lowercase with underscores exactly
- Omit the flag when the default (tcp_only) is fine
- Validate generated configs before deployment
When it happens
Trigger: Passing `--plugin-mode` (or equivalent) with anything other than the exact strings `tcp_only`, `udp_only`, or `tcp_and_udp`, e.g. `tcp`, `TCP_ONLY`, or `tcp-and-udp`.
Common situations: Users guessing the separator (`-` vs `_`), copying values from other proxy tools, or capitalizing the value from JSON/YAML config conventions.
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
- plugin-mode must be one of `tcp_only` (default), `udp_only`
- malformed `plugin_mode`, must be one of `tcp_only`, `udp_onl
- tcp-redir
- method
- dns
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/ac105b04c629d33d.
Report an issue: GitHub.