Fix sandbox cleanup race and improve warning formatting

Sandbox cleanup was spawned fire-and-forget in a scopeguard, causing DNS
resolution to be cancelled when the tokio runtime shut down before the
HTTP request completed. Now cleanup is awaited explicitly before
returning, with the scopeguard kept only as a safety net for panics.

Also: add blank line before the warning and color-code it yellow to
match other warnings. Update daytona-sdk to include full error source
chain in error messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-04 08:29:05 -05:00
parent 918847af91
commit bde59168c9
2 changed files with 25 additions and 19 deletions

22
Cargo.lock generated
View file

@ -986,7 +986,7 @@ checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "daytona-api-client"
version = "0.1.0"
source = "git+https://github.com/brynary/daytona-sdk-rust#5d370099a8dedb3d3c4d9d0bf31037e580e170a5"
source = "git+https://github.com/brynary/daytona-sdk-rust#b94db55e8bdd8b30b596038e554be0dcac19b40b"
dependencies = [
"reqwest 0.12.28",
"reqwest-middleware",
@ -1000,7 +1000,7 @@ dependencies = [
[[package]]
name = "daytona-sdk"
version = "0.1.0"
source = "git+https://github.com/brynary/daytona-sdk-rust#5d370099a8dedb3d3c4d9d0bf31037e580e170a5"
source = "git+https://github.com/brynary/daytona-sdk-rust#b94db55e8bdd8b30b596038e554be0dcac19b40b"
dependencies = [
"daytona-api-client",
"daytona-toolbox-client",
@ -1019,7 +1019,7 @@ dependencies = [
[[package]]
name = "daytona-toolbox-client"
version = "0.1.0"
source = "git+https://github.com/brynary/daytona-sdk-rust#5d370099a8dedb3d3c4d9d0bf31037e580e170a5"
source = "git+https://github.com/brynary/daytona-sdk-rust#b94db55e8bdd8b30b596038e554be0dcac19b40b"
dependencies = [
"reqwest 0.12.28",
"reqwest-middleware",
@ -1112,7 +1112,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -1190,7 +1190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -2438,7 +2438,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -2983,7 +2983,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
"windows-sys 0.59.0",
]
[[package]]
@ -3363,7 +3363,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -3431,7 +3431,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -4214,7 +4214,7 @@ dependencies = [
"getrandom 0.4.1",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -5064,7 +5064,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.48.0",
]
[[package]]

View file

@ -454,17 +454,13 @@ pub async fn run_command(
.await
.map_err(|e| anyhow::anyhow!("Failed to initialize sandbox: {e}"))?;
// Ensure cleanup runs even on error/panic
// Safety net: if we panic or return early, best-effort cleanup via spawn.
let sandbox_for_cleanup = Arc::clone(&sandbox);
let _cleanup_guard = scopeguard::guard((), move |()| {
// Best-effort cleanup — fire and forget in a blocking context
let cleanup_guard = scopeguard::guard((), move |()| {
let rt = tokio::runtime::Handle::try_current();
if let Ok(handle) = rt {
handle.spawn(async move {
if let Err(e) = sandbox_for_cleanup.cleanup().await {
tracing::warn!(error = %e, "Sandbox cleanup failed");
eprintln!("Warning: sandbox cleanup failed: {e}");
}
let _ = sandbox_for_cleanup.cleanup().await;
});
}
});
@ -746,7 +742,17 @@ pub async fn run_command(
print_final_output(&logs_dir, styles);
// 9. Exit code
// 9. Cleanup sandbox (defuse the scopeguard so we await properly)
scopeguard::ScopeGuard::into_inner(cleanup_guard);
if let Err(e) = sandbox.cleanup().await {
tracing::warn!(error = %e, "Sandbox cleanup failed");
eprintln!(
"\n{} sandbox cleanup failed: {e}",
styles.yellow.apply_to("Warning:")
);
}
// 10. Exit code
match outcome.status {
StageStatus::Success | StageStatus::PartialSuccess => Ok(()),
_ => {