shadowsocks/shadowsocks-rust · error
malformed `mode`, must be one of `tcp_only`, `udp_only` and
Error message
malformed `mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`
What it means
While parsing the server configuration (config.rs, mode section), the `mode` string is parsed into Mode. If it is not exactly one of `tcp_only`, `udp_only`, or `tcp_and_udp`, the parse fails and the library returns ErrorKind::Malformed. This keeps invalid global modes from silently defaulting to TCP-only.
Source
Thrown at crates/shadowsocks-service/src/config.rs:1788
// by checking all its remote servers if all of them supports IPv6.
let ip = if ipv6_first {
Ipv6Addr::LOCALHOST.into()
} else {
Ipv4Addr::LOCALHOST.into()
};
ServerAddr::from(SocketAddr::new(ip, local_port))
}
}
}
// Mode
let mut global_mode = Mode::TcpOnly;
if let Some(m) = config.mode {
match m.parse::<Mode>() {
Ok(xm) => global_mode = xm,
Err(..) => {
let e = Error::new(
ErrorKind::Malformed,
"malformed `mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
None,
);
return Err(e);
}
}
}
match config_type {
ConfigType::Local => {
// Standard config
if config.local_address.is_some() && config.local_port.unwrap_or(0) == 0 {
let err = Error::new(ErrorKind::MissingField, "missing `local_port`", None);
return Err(err);
}
if let Some(local_port) = config.local_port {View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Change `mode` in the config to exactly one of "tcp_only", "udp_only", or "tcp_and_udp" (all lowercase, with underscores).
- Remove the `mode` key entirely to accept the default (Mode::TcpOnly).
- Check for typos/casing when the value comes from an environment variable or template substitution.
Example fix
// before
{ "mode": "TCP-ONLY" }
// after
{ "mode": "tcp_only" } Defensive patterns
Strategy: validation
Validate before calling
const MODES: [&str; 3] = ["tcp_only", "udp_only", "tcp_and_udp"];
if let Some(m) = raw.get("mode") {
if !MODES.contains(&m.as_str().unwrap_or("")) {
return Err(format!("invalid mode: {m}; must be one of tcp_only, udp_only, tcp_and_udp"));
}
} Type guard
fn is_valid_mode(s: &str) -> bool {
matches!(s, "tcp_only" | "udp_only" | "tcp_and_udp")
} Try / catch
match Config::load(path) {
Ok(cfg) => start(cfg),
Err(e) if e.to_string().contains("malformed `mode`") => eprintln!("mode must be tcp_only, udp_only, or tcp_and_udp (lowercase)"),
Err(e) => return Err(e),
} Prevention
- Parse modes with Mode::from_str in a pre-check before writing configs.
- Avoid free-form mode strings from env vars; validate on input.
- Quote exact accepted values in config documentation.
When it happens
Trigger: Loading a config whose JSON `mode` field contains a string that Mode::from_str rejects, e.g. "tcp", "TCP_ONLY", "tcp-and-udp", or any typo; typically hit via Config::load / Config::from_str on a JSON config.
Common situations: Hand-written configs using wrong casing or abbreviations; older configs using legacy mode spellings; generated configs from third-party tools emitting non-canonical mode values.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- TCP mode have to be enabled for http
- missing `local_dns_addr` or `remote_dns_addr` in configurati
- missing `forward_addr` in configuration
- missing `local_port`
- `protocol` invalid
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/0a03dbedc5a18e5d.
Report an issue: GitHub.