{"record":{"id":"c9014bc0e4d8fade","repo":"zellij-org/zellij","slug":"failed-to-accept-reply-connection","errorCode":null,"errorMessage":"failed to accept reply connection","messagePattern":"failed to accept reply connection","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"zellij-server/src/lib.rs","lineNumber":943,"sourceCode":"                // It is not guaranteed that all platforms allow setting the sticky bit on sockets!\n                #[cfg(unix)]\n                drop(set_permissions(&socket_path, 0o1700));\n\n                // On Windows, named pipes are half-duplex, so we need a separate\n                // reply pipe for server→client messages.\n                #[cfg(windows)]\n                let reply_listener = zellij_utils::consts::ipc_bind_reply(&socket_path).unwrap();\n\n                for stream in listener.incoming() {\n                    match stream {\n                        Ok(stream) => {\n                            let mut os_input = os_input.clone();\n                            let client_id = session_state.write().unwrap().new_client();\n\n                            #[cfg(windows)]\n                            let reply_stream = reply_listener\n                                .accept()\n                                .expect(\"failed to accept reply connection\");\n\n                            #[cfg(windows)]\n                            let receiver = os_input\n                                .new_client_with_reply(client_id, stream, reply_stream)\n                                .unwrap();\n                            #[cfg(not(windows))]\n                            let receiver = os_input.new_client(client_id, stream).unwrap();\n\n                            let session_data = session_data.clone();\n                            let session_state = session_state.clone();\n                            let to_server = to_server.clone();\n                            thread::Builder::new()\n                                .name(\"server_router\".to_string())\n                                .spawn(move || {\n                                    route_thread_main(\n                                        session_data,\n                                        session_state,\n                                        os_input,","sourceCodeStart":925,"sourceCodeEnd":961,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-server/src/lib.rs#L925-L961","documentation":"Windows-only accept path: after a client connects to the main ipc socket, the server accepts the paired reply connection on the second named pipe bound via ipc_bind_reply, and this expect treats an accept error as fatal. The failure is almost always a pipe-state race - the client died between connecting to the main socket and the reply accept, the pipe handle was closed, or third-party software interfered with the named pipe. It runs inside the per-connection branch of the listener loop, so one bad accept panics the whole server accept thread.","triggerScenarios":"A Windows client that connects to the main socket but crashes or exits before the reply pipe is accepted; a stale pipe object from a previous server; antivirus/EDR hooking named pipes and breaking the handshake.","commonSituations":"Attaching from a client that dies mid-handshake; leftover pipes in the temp dir after unclean shutdown; security software intercepting IPC.","solutions":["Clear stale zellij ipc artifacts (the zellij socket temp dir) and retry the attach","Make sure client and server are the exact same zellij version - a mismatched handshake can drop the client","Temporarily exclude zellij from antivirus/EDR named-pipe interception to test","If the client itself crashes on connect, reproduce with logging and report it - the accept failure is then a symptom, not the cause"],"exampleFix":"// before\nlet reply_stream = reply_listener\n    .accept()\n    .expect(\"failed to accept reply connection\");\n\n// after - tolerate a lost client instead of killing the listener\nlet reply_stream = match reply_listener.accept() {\n    Ok(s) => s,\n    Err(e) => {\n        log::error!(\"reply accept failed ({e}); client likely vanished\");\n        continue;\n    }\n};","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let mut attempts = 0;\nlet reply_stream = loop {\n    match reply_listener.accept() {\n        Ok(s) => break s,\n        Err(e) if attempts < 3 => {\n            attempts += 1;\n            log::warn!(\"reply accept retry {attempts}: {e}\");\n            std::thread::sleep(std::time::Duration::from_millis(50));\n        }\n        Err(e) => {\n            log::error!(\"reply accept failed permanently: {e}; dropping this client\");\n            continue; // drop this client, keep the listener alive\n        }\n    }\n};","preventionTips":["Clean the zellij socket temp directory after unclean shutdowns","Pin client and server to the same zellij version","Beware EDR/antivirus products that open named pipes concurrently","Never let one connection's accept failure unwind the whole listener loop"],"tags":["rust","zellij","windows","named-pipes","ipc","accept"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}