{"record":{"id":"96267b9ca883cc7e","repo":"BloopAI/vibe-kanban","slug":"channel-already-has-an-active-session","errorCode":null,"errorMessage":"Channel already has an active session","messagePattern":"Channel already has an active session","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/embedded-ssh/src/handler.rs","lineNumber":63,"sourceCode":"        }\n    }\n\n    fn spawn_stdio_session(\n        &mut self,\n        channel_id: ChannelId,\n        command: Option<&str>,\n        session: &mut Session,\n    ) -> Result<(), anyhow::Error> {\n        tracing::debug!(\"Spawning stdio session (no PTY)\");\n        let state = self\n            .channels\n            .remove(&channel_id)\n            .ok_or_else(|| anyhow::anyhow!(\"Channel not found\"))?;\n\n        let env = match state {\n            ChannelState::Pending { channel: _, env } => env,\n            ChannelState::Active { .. } => {\n                anyhow::bail!(\"Channel already has an active session\");\n            }\n        };\n\n        let shell = std::env::var(\"SHELL\").unwrap_or_else(|_| \"/bin/sh\".to_string());\n        let mut cmd = Command::new(shell);\n        match command {\n            Some(cmd_str) => {\n                cmd.arg(\"-c\");\n                cmd.arg(cmd_str);\n            }\n            None => {\n                // Non-interactive shell reading commands from stdin.\n                cmd.arg(\"-s\");\n            }\n        }\n\n        cmd.stdin(Stdio::piped());\n        cmd.stdout(Stdio::piped());","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/embedded-ssh/src/handler.rs#L45-L81","documentation":"spawn_stdio_session spawns a local shell (or exec command) for an SSH channel, but only when the channel is in the Pending state. If the channel was already transitioned to Active by a prior shell/exec request, it is removed from the pending map and this bail fires. It guards against double session spawn per channel.","triggerScenarios":"Calling shell_request or exec_request twice on the same SSH channel; a client sending a second 'shell' or 'exec' request after a session already started on that channel.","commonSituations":"Misbehaving or buggy SSH clients issuing duplicate session requests; a client sending exec right after shell on the same channel; reconnect logic re-sending a request without opening a new channel.","solutions":["Open a new SSH channel before requesting a second shell/exec session","Fix client code so only one session request is made per channel","Check channel state (ChannelState::Active) client-side before re-issuing the request"],"exampleFix":"// before\nsession.request_shell(channel_id);\nsession.request_shell(channel_id); // bails: Channel already has an active session\n// after\nsession.open_channel().await?;\nsession.request_shell(new_channel_id).await?;","handlingStrategy":"validation","validationCode":"if channel_state_is_active(channel_id) { return Err(\"session already running; open a new channel\"); }","typeGuard":"fn is_pending(state: &ChannelState) -> bool { matches!(state, ChannelState::Pending { .. }) }","tryCatchPattern":"match spawn_stdio_session(ch).await { Err(e) if e.to_string().contains(\"Channel already has an active session\") => open_new_channel_and_retry(), other => other? }","preventionTips":["One session request per SSH channel, always","Open a fresh channel for shell vs exec","Track channel state client-side before sending session requests"],"tags":["ssh","session","channel-state"],"backgroundTag":"duplicate-session-on-channel","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}