From 3d806de059fd564312c65c3818b25661bcbff38b Mon Sep 17 00:00:00 2001 From: bravo1goingdark Date: Tue, 5 May 2026 22:22:12 +0530 Subject: [PATCH] fix(archive): make probe() async; remove block_on inside tokio runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Archiver::probe() was synchronous but called Handle::current().block_on() on an async S3 head request. The startup path that invokes it sits inside a tokio runtime (Server::run_inner is async, called from rt.block_on in keplor-cli/main.rs), and tokio explicitly disallows nested block_on — abort with 'Cannot start a runtime from within a runtime'. Anyone enabling --features s3 and configuring [archive] hits this immediately on startup; this is the first time we shipped the feature live. Make probe() async and .await it at the call site. Verified end-to-end against a Cloudflare R2 bucket: 'event archival configured — S3 connectivity verified' on first start. --- crates/keplor-server/src/server.rs | 2 +- crates/keplor-store/src/archive.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/keplor-server/src/server.rs b/crates/keplor-server/src/server.rs index b96ed77..401c406 100644 --- a/crates/keplor-server/src/server.rs +++ b/crates/keplor-server/src/server.rs @@ -301,7 +301,7 @@ impl PipelineServer { // Validate S3 connectivity at startup — fail fast on // bad credentials instead of discovering errors hours // later on the first archive cycle. - if let Err(e) = archiver.probe() { + if let Err(e) = archiver.probe().await { tracing::error!( error = %e, "S3 connectivity check failed — archival disabled. \ diff --git a/crates/keplor-store/src/archive.rs b/crates/keplor-store/src/archive.rs index 0bce70b..be26148 100644 --- a/crates/keplor-store/src/archive.rs +++ b/crates/keplor-store/src/archive.rs @@ -67,11 +67,11 @@ impl Archiver { /// Call at startup to fail fast on invalid credentials or /// unreachable endpoints instead of discovering errors hours /// later on the first archive cycle. - pub fn probe(&self) -> Result<(), StoreError> { + pub async fn probe(&self) -> Result<(), StoreError> { let probe_path = ObjPath::from(format!("{}/_probe", self.prefix)); // A HEAD on a non-existent key returns 404, not an auth error. // An auth failure returns 403. Both are valid S3 responses. - match tokio::runtime::Handle::current().block_on(self.client.head(&probe_path)) { + match self.client.head(&probe_path).await { Ok(_) => Ok(()), Err(object_store::Error::NotFound { .. }) => Ok(()), // 404 = reachable Err(e) => Err(StoreError::ArchiveS3(format!("probe failed: {e}"))),