diff --git a/hive-c0re/src/lifecycle.rs b/hive-c0re/src/lifecycle.rs index 69175a54..abbc94a7 100644 --- a/hive-c0re/src/lifecycle.rs +++ b/hive-c0re/src/lifecycle.rs @@ -438,7 +438,38 @@ pub async fn rebuild_no_meta( } run(&["update", &container, "--flake", &flake_ref]).await?; if was_running { - run(&["start", &container]).await + // Normal path: start into the new generation. The activation + // script runs inside the container to transition old → new. + // This can fail when packages are removed between generations — + // the old-generation activation references units that no longer + // exist in the new closure, causing systemd to exit non-zero. + // + // Fallback: kill + start (cold-start). `kill` SIGKILLs any + // lingering container processes so the next `start` enters a + // clean state without a generation transition, letting the + // activation succeed. + if let Err(start_err) = run(&["start", &container]).await { + tracing::warn!( + container = %container, + error = %start_err, + "start after rebuild failed (possible activation error); \ + retrying via kill + start" + ); + run(&["kill", &container]).await.unwrap_or_else(|e| { + tracing::warn!( + container = %container, + error = %e, + "kill before cold-start retry failed (ignored)" + ); + }); + run(&["start", &container]).await + .map_err(|e| anyhow::anyhow!( + "cold-start fallback also failed: {e:#} \ + (original start error: {start_err:#})" + )) + } else { + Ok(()) + } } else { Ok(()) }