{"record":{"id":"49eb67b063db6c9f","repo":"shadowsocks/shadowsocks-rust","slug":"plugin-shouldn-t-be-an-empty-string","errorCode":null,"errorMessage":"`plugin` shouldn't be an empty string","messagePattern":"`plugin` shouldn't be an empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/shadowsocks-service/src/config.rs","lineNumber":2916,"sourceCode":"        }\n\n        if self.config_type.is_manager() && self.manager.is_none() {\n            let err = Error::new(\n                ErrorKind::MissingField,\n                \"missing `manager_addr` and `manager_port` in configuration\",\n                None,\n            );\n            return Err(err);\n        }\n\n        for inst in &self.server {\n            let server = &inst.config;\n\n            // Plugin shouldn't be an empty string\n            if let Some(plugin) = server.plugin()\n                && plugin.plugin.trim().is_empty()\n            {\n                let err = Error::new(ErrorKind::Malformed, \"`plugin` shouldn't be an empty string\", None);\n                return Err(err);\n            }\n\n            // Server's domain name shouldn't be an empty string\n            match server.addr() {\n                ServerAddr::SocketAddr(sa) => {\n                    if sa.port() == 0 {\n                        let err = Error::new(ErrorKind::Malformed, \"`server_port` shouldn't be 0\", None);\n                        return Err(err);\n                    }\n\n                    if self.config_type.is_local() {\n                        // Only server could bind to INADDR_ANY\n                        let ip = sa.ip();\n                        if ip.is_unspecified() {\n                            let err = Error::new(\n                                ErrorKind::Malformed,\n                                \"`server` shouldn't be an unspecified address (INADDR_ANY)\",","sourceCodeStart":2898,"sourceCodeEnd":2934,"githubUrl":"https://github.com/shadowsocks/shadowsocks-rust/blob/8eb0f0a65b1d976ab6bed5787327ef86529b0435/crates/shadowsocks-service/src/config.rs#L2898-L2934","documentation":"Each server instance's SIP003/SIP003u plugin, when present, must be a non-empty (non-whitespace) string. Config::check walks all server configs and rejects any whose plugin field is Some but trims to empty with ErrorKind::Malformed.","triggerScenarios":"A server config with plugin = \"\" (or whitespace only) — typically from an ss:// URL with an empty plugin= parameter, SIP008 server entries with empty plugin fields, or a programmatically constructed ServerConfig where plugin was set to Some(\"\").","commonSituations":"Copy-pasted ss:// links where the plugin parameter was deleted but the ?plugin= key left empty; environment-variable-driven launch scripts that pass PLUGIN=\"\"; SIP008 subscription data with blank plugin strings.","solutions":["Set the plugin field to a real plugin name (e.g. \"obfs-local;obfs=http;obfs-host=example.com\") or remove the plugin key entirely so it is None.","In launch scripts, omit the --plugin flag when the PLUGIN variable is empty instead of passing an empty value.","Fix the ss:// URL or SIP008 source so plugin= is either fully specified or absent."],"exampleFix":"// before\n{ \"server\": \"1.2.3.4\", \"server_port\": 8388, \"password\": \"pw\", \"method\": \"aes-256-gcm\", \"plugin\": \"\" }\n\n// after\n{ \"server\": \"1.2.3.4\", \"server_port\": 8388, \"password\": \"pw\", \"method\": \"aes-256-gcm\", \"plugin\": \"obfs-local;obfs=http;obfs-host=example.com\" }","handlingStrategy":"validation","validationCode":"// Before building ss:// URLs or ServerConfig\nlet plugin: Option<&str> = maybe_plugin.as_deref();\nif let Some(p) = plugin {\n    assert!(!p.trim().is_empty(), \"plugin must be a non-empty string\");\n}","typeGuard":"fn has_plugin(p: &Option<String>) -> bool {\n    matches!(p, Some(s) if !s.trim().is_empty())\n}","tryCatchPattern":null,"preventionTips":["In scripts, drop the --plugin flag entirely when PLUGIN is empty instead of passing \"\".","Never hand-edit the ?plugin= query parameter of ss:// URLs — regenerate the link.","Sanitize SIP008 subscription data: convert empty plugin strings to absent fields."],"tags":["config","plugin","validation","sip003"],"backgroundTag":"empty-required-field","analyzedSha":"8eb0f0a65b1d976ab6bed5787327ef86529b0435","analyzedAt":"2026-09-09T12:20:43.168Z","contentChangedAt":"2026-09-09T12:20:43.168Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}