{"record":{"id":"4e8d9371c9f8695a","repo":"nautechsystems/nautilus_trader","slug":"socket-suffix-cannot-be-empty-suffix-is-required","errorCode":null,"errorMessage":"Socket suffix cannot be empty: suffix is required for message framing","messagePattern":"Socket suffix cannot be empty: suffix is required for message framing","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/network/src/socket/client.rs","lineNumber":124,"sourceCode":"    reconnect_attempt_count: u32,\n    state_sink: Option<SocketStateSink>,\n}\n\nimpl SocketClientInner {\n    /// Connects to a URL with the specified configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if connection fails or configuration is invalid.\n    async fn connect_url(\n        config: SocketConfig,\n        state_sink: Option<SocketStateSink>,\n    ) -> anyhow::Result<Self> {\n        install_cryptographic_provider();\n\n        // Validate suffix is non-empty to prevent panic in read loop (windows(0) panics)\n        if config.suffix.is_empty() {\n            anyhow::bail!(\"Socket suffix cannot be empty: suffix is required for message framing\");\n        }\n\n        // Adapters build this config by struct literal, bypassing the builder, so this is the only\n        // place the field invariants are enforced for them.\n        config.validate()?;\n\n        let connect_timeout = Duration::from_millis(config.connect_timeout_ms.unwrap_or(10_000));\n        let reconnect_backoff = ExponentialBackoff::new(\n            Duration::from_millis(config.reconnect_delay_initial_ms.unwrap_or(2_000)),\n            Duration::from_millis(config.reconnect_delay_max_ms.unwrap_or(30_000)),\n            config.reconnect_backoff_factor.unwrap_or(1.5),\n            config.reconnect_jitter_ms.unwrap_or(100),\n            true, // immediate-first\n        )?;\n        let connector = if let Some(dir) = &config.certs_dir {\n            let config = create_tls_config_from_certs_dir(Path::new(dir), false)?;\n            Some(Arc::new(config))\n        } else {","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/network/src/socket/client.rs#L106-L142","documentation":"SocketClient requires config.suffix to be a non-empty string because the suffix is appended to messages for framing; an empty suffix would cause the read loop's windows(0) to panic. connect_url validates this first, before config.validate(), since adapters may construct the config by struct literal and bypass builder checks.","triggerScenarios":"Connecting with a SocketConfig whose suffix field is set to \"\" — typically by struct-literal construction in an adapter, a config file with an empty suffix, or a placeholder left unfilled.","commonSituations":"Custom exchange adapters forgetting to set the message-terminating suffix (e.g. \"\\n\" for line-framed sockets); YAML/TOML config with suffix: '' ; migrating code to a newer client that added this invariant.","solutions":["Set a non-empty framing suffix in the socket config (e.g. \"\\n\" for newline-delimited messages).","Match the suffix the remote endpoint actually uses to delimit messages.","If the config is built in code, construct it through the builder to catch invariants earlier.","Add a startup-time config check that fails fast with a clearer application-level message."],"exampleFix":"// before\nlet config = SocketConfig { url, suffix: String::new(), .. };\n// after\nlet config = SocketConfig { url, suffix: \"\\n\".to_string(), .. };","handlingStrategy":"validation","validationCode":"// Rust: fail fast at config load\nif config.suffix.is_empty() {\n    return Err(anyhow::anyhow!(\"socket suffix must be non-empty (e.g. \\\"\\\\n\\\")\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set the framing suffix matching the remote protocol (\"\\n\", \"\\r\\n\", etc.).","Build socket configs via the builder rather than struct literals.","Validate socket config fields at application startup.","Never leave placeholder empty strings in config templates."],"tags":["rust","network","websocket","socket","config"],"backgroundTag":"empty-required-field","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}