{"record":{"id":"9bc06bbae42e8cc0","repo":"BloopAI/vibe-kanban","slug":"child-process-not-found-for-execution","errorCode":null,"errorMessage":"Child process not found for execution","messagePattern":"Child process not found for execution","errorType":"exception","errorClass":"ContainerError","httpStatus":null,"severity":"warning","filePath":"crates/local-deployment/src/container.rs","lineNumber":1414,"sourceCode":"        }\n\n        // Spawn unified exit monitor: watches OS exit and optional executor signal\n        let hn = self.spawn_exit_monitor(&execution_process.id, spawned.exit_signal);\n        self.add_exit_monitor_handle(execution_process.id, hn).await;\n\n        Ok(())\n    }\n\n    async fn stop_execution(\n        &self,\n        execution_process: &ExecutionProcess,\n        status: ExecutionProcessStatus,\n    ) -> Result<(), ContainerError> {\n        let child = self\n            .get_child_from_store(&execution_process.id)\n            .await\n            .ok_or_else(|| {\n                ContainerError::Other(anyhow!(\"Child process not found for execution\"))\n            })?;\n        let exit_code = if status == ExecutionProcessStatus::Completed {\n            Some(0)\n        } else {\n            None\n        };\n\n        ExecutionProcess::update_completion(&self.db.pool, execution_process.id, status, exit_code)\n            .await?;\n\n        // Try graceful cancellation first, then force kill\n        if let Some(cancel) = self.take_cancellation_token(&execution_process.id).await {\n            cancel.cancel();\n\n            // Wait for exit monitor to finish gracefully\n            if let Some(monitor_handle) = self.take_exit_monitor_handle(&execution_process.id).await\n            {\n                match tokio::time::timeout(Duration::from_secs(5), monitor_handle).await {","sourceCodeStart":1396,"sourceCodeEnd":1432,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/local-deployment/src/container.rs#L1396-L1432","documentation":"stop_execution fetches the live child process handle from the execution's store via get_child_from_store and errors when no child is registered for the execution id. A child only exists while the process is running and tracked; after exit, cleanup, or if it never attached, there is nothing to stop. This is an expected consequence of stopping an already-finished or unknown execution.","triggerScenarios":"Calling stop_execution (directly or via kill_all_running_processes, stop_execution_process, delete_workspace, start_dev_server, archive_workspace, try_stop) with an execution id whose child was already reaped, was never tracked, or whose MsgStore was cleared.","commonSituations":"Double-stop of the same execution; stopping a process that already exited; delete_workspace/archive racing with the process finishing naturally; restarting the server so in-memory children were lost while DB rows remain.","solutions":["Check the execution's status first and skip the stop call if it is already Completed/Failed/Killed.","Treat this error as benign and continue — the goal (process not running) is already achieved.","Avoid double-stopping: track stopped execution ids in the caller or make stop idempotent.","If after a server restart, the child cannot be stopped in-memory; rely on process-status reconciliation instead."],"exampleFix":"// before\ncontainer.stop_execution(&process, ExecutionProcessStatus::Killed).await?;\n// after\nif matches!(process.status, ExecutionProcessStatus::Running | ExecutionProcessStatus::Paused) {\n    container.stop_execution(&process, ExecutionProcessStatus::Killed).await?;\n}","handlingStrategy":"type-guard","validationCode":"if !matches!(process.status, ExecutionProcessStatus::Running) {\n    return Ok(()); // nothing to stop\n}","typeGuard":"fn is_stoppable(status: &ExecutionProcessStatus) -> bool {\n    matches!(status, ExecutionProcessStatus::Running | ExecutionProcessStatus::Paused)\n}","tryCatchPattern":"match container.stop_execution(&proc, ExecutionProcessStatus::Killed).await {\n    Err(ContainerError::Other(e)) if e.to_string().contains(\"Child process not found\") => Ok(()), // already gone\n    other => other,\n}","preventionTips":["Check process status before calling stop_execution.","Make stop operations idempotent on the caller side.","Avoid stopping executions after workspace delete/archive completes.","Reconcile DB process status with in-memory children after restart."],"tags":["process","lifecycle","idempotency"],"backgroundTag":"process-not-found","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}