{"record":{"id":"d3fbf29130a2fc47","repo":"zellij-org/zellij","slug":"could-not-enable-raw-mode","errorCode":null,"errorMessage":"could not enable raw mode","messagePattern":"could not enable raw mode","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"zellij-client/src/os_input_output.rs","lineNumber":172,"sourceCode":"    fn env_variable(&self, _name: &str) -> Option<String> {\n        None\n    }\n    /// Returns an async stdin reader that can be polled in tokio::select\n    fn get_async_stdin_reader(&self) -> Box<dyn AsyncStdin> {\n        Box::new(AsyncStdinReader::new())\n    }\n    /// Returns an async signal listener that can be polled in tokio::select\n    fn get_async_signal_listener(&self) -> io::Result<Box<dyn AsyncSignals>> {\n        Ok(Box::new(AsyncSignalListener::new()?))\n    }\n}\n\nimpl ClientOsApi for ClientOsInputOutput {\n    fn get_terminal_size(&self) -> Size {\n        get_terminal_size()\n    }\n    fn set_raw_mode(&mut self) {\n        crossterm::terminal::enable_raw_mode().expect(\"could not enable raw mode\");\n    }\n    fn unset_raw_mode(&self) -> Result<(), std::io::Error> {\n        crossterm::terminal::disable_raw_mode()\n    }\n    fn box_clone(&self) -> Box<dyn ClientOsApi> {\n        Box::new((*self).clone())\n    }\n    fn update_session_name(&mut self, new_session_name: String) {\n        *self.session_name.lock().unwrap() = Some(new_session_name);\n    }\n    fn read_from_stdin(&mut self) -> Result<Vec<u8>, &'static str> {\n        let session_name_at_calltime = { self.session_name.lock().unwrap().clone() };\n        // here we wait for a lock in case another thread is holding stdin\n        // this can happen for example when switching sessions, the old thread will only be\n        // released once it sees input over STDIN\n        //\n        // when this happens, we detect in the other thread that our session is ended (by comparing\n        // the session name at the beginning of the call and the one after we read from STDIN), and","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-client/src/os_input_output.rs#L154-L190","documentation":"set_raw_mode() is called by the client on startup and expects crossterm::terminal::enable_raw_mode() to succeed: raw mode disables echo, line buffering and canonical signal processing so the client can read every key itself. It fails when the termios attributes cannot be read or written - stdin/stdout is not a tty, there is no controlling terminal, or the tty device is inaccessible. Because every keystroke flows through raw mode, the client treats this as fatal immediately.","triggerScenarios":"Launching the client with stdin or stdout redirected (scripts, cron, CI, `zellij < file`), running without a controlling terminal (detached setsid, some docker exec sessions), or a termios ioctl failure on the tty device.","commonSituations":"ssh -T; docker exec without -t; IDE task runners and watch tools that capture stdio; broken/absent TERM or tty permission problems on multi-user machines.","solutions":["Run zellij from a real interactive terminal with a tty on stdin and stdout","Guard scripted launches with `[ -t 0 ] && [ -t 1 ]` or `tty -s` before starting zellij","Wrap non-interactive contexts in a pty allocator such as `script -qec \"zellij attach <name>\" /dev/null`","Fix tty device permissions or the stale controlling terminal (re-login) if it still fails interactively"],"exampleFix":"// before\nfn set_raw_mode(&mut self) {\n    crossterm::terminal::enable_raw_mode().expect(\"could not enable raw mode\");\n}\n\n// after - check the precondition, then make failure non-fatal\nuse std::io::IsTerminal;\nfn set_raw_mode(&mut self) -> std::io::Result<()> {\n    if !std::io::stdin().is_terminal() {\n        return Err(std::io::Error::new(std::io::ErrorKind::NotConnected, \"stdin is not a tty\"));\n    }\n    crossterm::terminal::enable_raw_mode()\n}","handlingStrategy":"validation","validationCode":"use std::io::IsTerminal;\n\nfn has_usable_tty() -> bool {\n    std::io::stdin().is_terminal() && std::io::stdout().is_terminal()\n}\n\n// before calling any client API that enters raw mode\nif !has_usable_tty() {\n    eprintln!(\"zellij requires a tty on stdin and stdout\");\n    std::process::exit(1);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never launch the client from cron/CI without a pty","Check `[ -t 0 ] && [ -t 1 ]` in wrapper scripts","Use ssh -tt / docker exec -it to force pty allocation","Enable raw mode at start and disable it on every exit path, including panics"],"tags":["rust","zellij","raw-mode","tty","crossterm","terminal"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}