{"record":{"id":"6629dd9b57bb7eb6","repo":"rathole-org/rathole","slug":"neither-of-server-or-client-is-defined","errorCode":null,"errorMessage":"Neither of `[server]` or `[client]` is defined","messagePattern":"Neither of `\\[server\\]` or `\\[client\\]` is defined","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/config.rs","lineNumber":252,"sourceCode":"pub struct Config {\n    pub server: Option<ServerConfig>,\n    pub client: Option<ClientConfig>,\n}\n\nimpl Config {\n    fn from_str(s: &str) -> Result<Config> {\n        let mut config: Config = toml::from_str(s).with_context(|| \"Failed to parse the config\")?;\n\n        if let Some(server) = config.server.as_mut() {\n            Config::validate_server_config(server)?;\n        }\n\n        if let Some(client) = config.client.as_mut() {\n            Config::validate_client_config(client)?;\n        }\n\n        if config.server.is_none() && config.client.is_none() {\n            Err(anyhow!(\"Neither of `[server]` or `[client]` is defined\"))\n        } else {\n            Ok(config)\n        }\n    }\n\n    fn validate_server_config(server: &mut ServerConfig) -> Result<()> {\n        // Validate services\n        for (name, s) in &mut server.services {\n            s.name = name.clone();\n            if s.token.is_none() {\n                s.token = server.default_token.clone();\n                if s.token.is_none() {\n                    bail!(\"The token of service {} is not set\", name);\n                }\n            }\n        }\n\n        Config::validate_transport_config(&server.transport, true)?;","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/rathole-org/rathole/blob/a292f7ed5402f840415fc6a53827da2f34337856/src/config.rs#L234-L270","documentation":"The TOML config file parsed successfully but defines neither a `[server]` nor a `[client]` section, so there is nothing for rathole to run. `Config::from_str` requires at least one of the two role sections to be present and returns this error otherwise.","triggerScenarios":"Calling `Config::from_str` (or running the binary) with a config whose top-level tables are only things like `[transport]`/`[network]` (shared config include), a completely empty file, a config where the sections are misspelled or nested under another table, or a file with only comments.","commonSituations":"Users split config into a common file plus server/client files and run the binary against only the common file; typos like `[Server]` (case) or accidentally indenting the section so it lands inside another table; pointing `-c` at the wrong file.","solutions":["Add a `[server]` or `[client]` section to the config file passed to the binary","Check that the config file you actually passed (-c path) is the role-specific one, not only the shared/common file","Verify section headers are top-level, correctly spelled lowercase `[server]`/`[client]`, and not indented under another table"],"exampleFix":"# before\n[transport]\ntype = \"tcp\"\n\n# after\n[client]\nremote_addr = \"example.com:2333\"\n\n[client.services]\nssh = { local_addr = \"127.0.0.1:22\" }","handlingStrategy":"validation","validationCode":"let cfg = std::fs::read_to_string(path)?;\nlet raw: toml::Value = toml::from_str(&cfg)?;\nif raw.get(\"server\").is_none() && raw.get(\"client\").is_none() {\n    return Err(format!(\"{}: config has neither [server] nor [client]\", path));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run the binary against a role-specific config, not just the shared/common one","Keep `[server]`/`[client]` headers top-level and lowercase","Validate the config with a dry run before deploying"],"tags":["config","rust","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"a292f7ed5402f840415fc6a53827da2f34337856","analyzedAt":"2026-09-07T09:56:55.739Z","contentChangedAt":"2026-09-07T09:56:55.739Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}