{"record":{"id":"174d798c14af55fe","repo":"BloopAI/vibe-kanban","slug":"channel-not-found","errorCode":null,"errorMessage":"Channel not found","messagePattern":"Channel not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/embedded-ssh/src/handler.rs","lineNumber":58,"sourceCode":"    pub fn new(relay_signing: RelaySigningService) -> Self {\n        Self {\n            relay_signing,\n            channels: HashMap::new(),\n            tcpip_forwards: HashMap::new(),\n        }\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\");","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/embedded-ssh/src/handler.rs#L40-L76","documentation":"In the embedded SSH server, spawn_stdio_session removes the channel's state from the handler's channels map before wiring up a shell/exec stdio session. If the channel id is absent from the map, there is no pending channel to promote, so it fails with 'Channel not found'. This indicates the request references a channel that was never opened or was already consumed/closed.","triggerScenarios":"A shell_request or exec_request arrives with a channel_id not present in self.channels — e.g. the channel was already removed by a previous spawn (double exec on one channel), a close/EOF event removed it first, or a client sends exec on a channel that failed to open.","commonSituations":"Buggy or aggressive SSH clients issuing exec/shell twice on the same channel, race between channel close and request handling, or a server restart clearing in-memory channel state while the client still believes the channel is open.","solutions":["Ensure each SSH channel runs at most one shell/exec request; open a new channel for each command.","Check that channel_open_handler registered the channel in self.channels before the client sends exec/shell.","Add logging to confirm the channel_id used in the request matches the one created during open; look for id mismatch or reuse.","If a race with channel close is suspected, guard the handler so close/eof events and spawn_stdio_session don't consume the entry concurrently."],"exampleFix":"// before (client reusing one channel)\nchannel.exec(\"ls\");\nchannel.exec(\"pwd\"); // second exec -> Channel not found\n// after\nchannel.exec(\"ls\");\nlet channel2 = session.channel_open()?;\nchannel2.exec(\"pwd\");","handlingStrategy":"validation","validationCode":"// client side: open a fresh channel per request\nlet channel = session.channel_open()?; // register before exec/shell\nchannel.exec(\"command\")?; // exactly one request per channel","typeGuard":null,"tryCatchPattern":"match handler.spawn_stdio_session(session, channel_id, ...) {\n    Ok(()) => {},\n    Err(e) if e.to_string() == \"Channel not found\" => {\n        tracing::debug!(channel_id, \"exec on unknown/closed channel; ignoring\");\n        // send SSH channel failure rather than tearing down the session\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Issue at most one exec/shell per SSH channel; open a new channel for each command.","Ensure the channel was successfully opened before sending requests.","Avoid racing channel close/eof with request handling in custom clients.","Log channel ids at open and request time to spot reuse or mismatch."],"tags":["ssh","channel-state","race-condition"],"backgroundTag":"ssh-channel-not-found","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}