{"record":{"id":"046a889ab5a83bca","repo":"valeriansaliou/sonic","slug":"brokenpipe-046a88","errorCode":null,"errorMessage":"BrokenPipe","messagePattern":"BrokenPipe","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"critical","filePath":"client/src/multiplexer.rs","lineNumber":56,"sourceCode":"\n        let poll_waker = Arc::new(mio::Waker::new(poll.registry(), mio::Token(usize::MAX))?);\n\n        // TODO: Do not auto-start? So one can spawn the task differently or\n        //   observe events (e.g. in tests)?\n        let event_loop_handle = std::thread::spawn(move || run_event_loop(&mut poll, rx));\n\n        Ok(Self {\n            _event_loop_handle: event_loop_handle,\n            tx,\n            poll_waker,\n        })\n    }\n\n    pub(crate) fn attach<C: SonicConnectionTrait + 'static>(&self, conn: C) -> std::io::Result<()> {\n        if let Err(error) =\n            (self.tx).send_timeout(MultiplexerTask::Attach(Box::new(conn)), SEND_TIMEOUT)\n        {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::BrokenPipe,\n                error.to_string(),\n            ));\n        };\n        self.poll_waker.wake()\n    }\n}\n\npub(crate) trait SonicConnectionTrait: AsMut<mio::net::TcpStream> + Send {\n    fn wants_to_write(&self) -> bool;\n\n    fn wants_to_read(&self) -> bool;\n\n    fn interest(&self) -> Option<mio::Interest> {\n        match (self.wants_to_write(), self.wants_to_read()) {\n            (false, false) => None,\n            (true, false) => Some(mio::Interest::WRITABLE),\n            (false, true) => Some(mio::Interest::READABLE),","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/valeriansaliou/sonic/blob/e6a72da6a532bc3f31d7d93ef1e0964a1a5d01b2/client/src/multiplexer.rs#L38-L74","documentation":"attach sends a MultiplexerTask::Attach message to the multiplexer task with send_timeout(SEND_TIMEOUT). A timeout means the multiplexer task is not running, so the new connection cannot be registered; it is surfaced as ErrorKind::BrokenPipe. connect() calls this, so a failed attach aborts connection establishment.","triggerScenarios":"Calling connect (which calls attach) when the multiplexer task has already exited — e.g. the previous connection killed it, the runtime shut it down, or attach is invoked after multiplexer shutdown; also when the multiplexer mailbox is full and never drains.","commonSituations":"Reconnect attempts after the multiplexer task died from a prior connection error; attaching connections from multiple threads during shutdown; runtime teardown racing with new connect() calls.","solutions":["Recreate the multiplexer/client instead of reusing a dead one; attach requires a live multiplexer task.","Ensure connect() is not called after the client was shut down/dropped; guard with a state check.","Keep the runtime alive while connections are being established.","If timeouts are marginal under load, ensure the multiplexer task isn't starved rather than raising SEND_TIMEOUT blindly."],"exampleFix":"// before\nlet conn = MyConn::connect(addr)?;\nmultiplexer.attach(conn)?;\n// after\nmatch multiplexer.attach(conn) {\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {\n        // multiplexer task is dead; rebuild the client\n        let client = Client::connect(addr)?;\n    }\n    Ok(()) => {}\n}","handlingStrategy":"try-catch","validationCode":"// Attach only to a live multiplexer:\nif multiplexer_task_handle.is_finished() {\n    return Err(\"multiplexer task dead; rebuild the client\".into());\n}","typeGuard":"fn is_broken_pipe(e: &std::io::Error) -> bool {\n    e.kind() == std::io::ErrorKind::BrokenPipe\n}","tryCatchPattern":"match multiplexer.attach(conn) {\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {\n        // multiplexer is gone: rebuild client from scratch\n        let client = Client::connect(addr)?;\n    }\n    other => other?,\n}","preventionTips":["Never call connect/attach on a client whose multiplexer task already exited","Serialize shutdown vs. new connection setup to avoid races","Rebuild the whole client (not just the connection) after multiplexer death","Keep the runtime alive during connection establishment"],"tags":["broken-pipe","multiplexer","connection","rust"],"backgroundTag":"broken-pipe-channel-closed","analyzedSha":"e6a72da6a532bc3f31d7d93ef1e0964a1a5d01b2","analyzedAt":"2026-09-01T14:10:00.384Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T20:17:18.057Z"}