From 666f80564fbc32d0321ed6740ddb9b233a929dc3 Mon Sep 17 00:00:00 2001 From: "user.email" Date: Mon, 26 Oct 2026 01:57:16 -0700 Subject: [PATCH] seaweed-volume: answer 404 for missing volumes in proxy-mode lookups Mirror Go's LookupVolumeId: a per-volume error in the master's lookup reply (e.g. a missing volume) means "no locations" and answers 404 downstream, not a lookup failure that proxy mode answers 500. --- seaweed-volume/src/server/handlers.rs | 43 ++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/seaweed-volume/src/server/handlers.rs b/seaweed-volume/src/server/handlers.rs index 285d92697e..6db0a3c68a 100644 --- a/seaweed-volume/src/server/handlers.rs +++ b/seaweed-volume/src/server/handlers.rs @@ -559,10 +559,13 @@ async fn lookup_volume( .json() .await .map_err(|e| format!("lookup parse failed: {}", e))?; + // Mirror Go: a per-volume error in the master reply (e.g. a missing + // volume) means "no locations" and answers 404 downstream — it does not + // surface as a lookup failure, which proxy mode answers 500. if let Some(err) = result.error && !err.is_empty() { - return Err(err); + tracing::debug!("volume {} lookup: {}", volume_id, err); } Ok(result.locations.unwrap_or_default()) } @@ -5364,6 +5367,44 @@ mod tests { server.abort(); } + /// The master answers a missing volume with 404 and a per-volume `error` + /// in the lookup body. Go's LookupResult.Error never becomes the Go error + /// return — the volume server treats it as empty locations and replies + /// 404. Surfacing it as Err would make proxy-mode reads answer 500. + #[tokio::test] + async fn test_lookup_volume_missing_volume_returns_empty() { + use axum::{Router, routing::get}; + + let app = Router::new().route( + "/dir/lookup", + get(|| async move { + ( + axum::http::StatusCode::NOT_FOUND, + axum::Json(serde_json::json!({ + "volumeOrFileId": "7", + "error": "volume id 7 not found" + })), + ) + }), + ); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let addr = listener.local_addr().unwrap(); + let server = tokio::spawn(async move { + axum::serve(listener, app).await.unwrap(); + }); + + let master_url = format!("127.0.0.1:{}", addr.port()); + let client = reqwest::Client::new(); + + let locations = super::lookup_volume(&client, "http", &master_url, 7) + .await + .expect("missing volume is not a lookup failure"); + assert!(locations.is_empty()); + + server.abort(); + } + /// A body streaming `data_size` bytes of `file` from offset 0. fn file_streaming_body( file: &std::fs::File,