BloopAI/vibe-kanban · warning · ContainerError
Child process not found for execution
Error message
Child process not found for execution
What it means
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.
Source
Thrown at crates/local-deployment/src/container.rs:1414
}
// Spawn unified exit monitor: watches OS exit and optional executor signal
let hn = self.spawn_exit_monitor(&execution_process.id, spawned.exit_signal);
self.add_exit_monitor_handle(execution_process.id, hn).await;
Ok(())
}
async fn stop_execution(
&self,
execution_process: &ExecutionProcess,
status: ExecutionProcessStatus,
) -> Result<(), ContainerError> {
let child = self
.get_child_from_store(&execution_process.id)
.await
.ok_or_else(|| {
ContainerError::Other(anyhow!("Child process not found for execution"))
})?;
let exit_code = if status == ExecutionProcessStatus::Completed {
Some(0)
} else {
None
};
ExecutionProcess::update_completion(&self.db.pool, execution_process.id, status, exit_code)
.await?;
// Try graceful cancellation first, then force kill
if let Some(cancel) = self.take_cancellation_token(&execution_process.id).await {
cancel.cancel();
// Wait for exit monitor to finish gracefully
if let Some(monitor_handle) = self.take_exit_monitor_handle(&execution_process.id).await
{
match tokio::time::timeout(Duration::from_secs(5), monitor_handle).await {View on GitHub (pinned to 4deb7eca8f)
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.
Example fix
// before
container.stop_execution(&process, ExecutionProcessStatus::Killed).await?;
// after
if matches!(process.status, ExecutionProcessStatus::Running | ExecutionProcessStatus::Paused) {
container.stop_execution(&process, ExecutionProcessStatus::Killed).await?;
} Defensive patterns
Strategy: type-guard
Validate before calling
if !matches!(process.status, ExecutionProcessStatus::Running) {
return Ok(()); // nothing to stop
} Type guard
fn is_stoppable(status: &ExecutionProcessStatus) -> bool {
matches!(status, ExecutionProcessStatus::Running | ExecutionProcessStatus::Paused)
} Try / catch
match container.stop_execution(&proc, ExecutionProcessStatus::Killed).await {
Err(ContainerError::Other(e)) if e.to_string().contains("Child process not found") => Ok(()), // already gone
other => other,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot run script while another process is running
- MsgStore not found for execution
- Timeout: process took more than 30 seconds to start
- Child process has no stdout
- Child process has no stdin
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/9bc06bbae42e8cc0.
Report an issue: GitHub.