fix(appd): broadcast AppState::Stopped on runtime spawn failure

If WEFT_RUNTIME_BIN is set but the binary cannot be spawned (missing,
not executable, etc.), supervise() now transitions the session to
Stopped and broadcasts AppState::Stopped instead of returning an error
that left the session permanently stuck in Starting.
This commit is contained in:
Marco Allegretti 2026-03-11 11:45:17 +01:00
parent 71c6741fae
commit 7a07e46c55

View file

@ -1,6 +1,5 @@
use std::time::Duration; use std::time::Duration;
use anyhow::Context;
use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::io::{AsyncBufReadExt, BufReader};
use crate::Registry; use crate::Registry;
@ -22,14 +21,26 @@ pub(crate) async fn supervise(
} }
}; };
let mut child = tokio::process::Command::new(&bin) let mut child = match tokio::process::Command::new(&bin)
.arg(app_id) .arg(app_id)
.arg(session_id.to_string()) .arg(session_id.to_string())
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.stdin(std::process::Stdio::null()) .stdin(std::process::Stdio::null())
.spawn() .spawn()
.with_context(|| format!("spawn {bin}"))?; {
Ok(c) => c,
Err(e) => {
tracing::warn!(session_id, %app_id, error = %e, "failed to spawn runtime; marking session stopped");
let mut reg = registry.lock().await;
reg.set_state(session_id, AppStateKind::Stopped);
let _ = reg.broadcast().send(Response::AppState {
session_id,
state: AppStateKind::Stopped,
});
return Ok(());
}
};
let stdout = child.stdout.take().expect("stdout piped"); let stdout = child.stdout.take().expect("stdout piped");
let stderr = child.stderr.take().expect("stderr piped"); let stderr = child.stderr.take().expect("stderr piped");