From 7ef8bafd519c4e25bd240f138b782dbc7faa9ef8 Mon Sep 17 00:00:00 2001 From: Will Jones Date: Tue, 4 Nov 2025 15:31:45 -0800 Subject: [PATCH] feat: add `source` to TableNotFound errors (#2765) This will make it easier to see if there are underlying problems. We should see the actual object store HTTP request error within the error chain after this. --- java/core/lancedb-jni/src/error.rs | 9 ++++--- rust/lancedb/src/database/listing.rs | 1 + rust/lancedb/src/error.rs | 4 ++- rust/lancedb/src/remote/db.rs | 7 ++--- rust/lancedb/src/remote/table.rs | 38 ++++++++++++++++++++++------ rust/lancedb/src/table.rs | 2 ++ 6 files changed, 44 insertions(+), 17 deletions(-) diff --git a/java/core/lancedb-jni/src/error.rs b/java/core/lancedb-jni/src/error.rs index dcb8b855..452adce9 100644 --- a/java/core/lancedb-jni/src/error.rs +++ b/java/core/lancedb-jni/src/error.rs @@ -51,8 +51,11 @@ pub enum Error { DatasetAlreadyExists { uri: String, location: Location }, #[snafu(display("Table '{name}' already exists"))] TableAlreadyExists { name: String }, - #[snafu(display("Table '{name}' was not found"))] - TableNotFound { name: String }, + #[snafu(display("Table '{name}' was not found: {source}"))] + TableNotFound { + name: String, + source: Box, + }, #[snafu(display("Invalid table name '{name}': {reason}"))] InvalidTableName { name: String, reason: String }, #[snafu(display("Embedding function '{name}' was not found: {reason}, {location}"))] @@ -191,7 +194,7 @@ impl From for Error { message, location: std::panic::Location::caller().to_snafu_location(), }, - lancedb::Error::TableNotFound { name } => Self::TableNotFound { name }, + lancedb::Error::TableNotFound { name, source } => Self::TableNotFound { name, source }, lancedb::Error::TableAlreadyExists { name } => Self::TableAlreadyExists { name }, lancedb::Error::EmbeddingFunctionNotFound { name, reason } => { Self::EmbeddingFunctionNotFound { diff --git a/rust/lancedb/src/database/listing.rs b/rust/lancedb/src/database/listing.rs index bc1af880..f3d24740 100644 --- a/rust/lancedb/src/database/listing.rs +++ b/rust/lancedb/src/database/listing.rs @@ -455,6 +455,7 @@ impl ListingDatabase { // `remove_dir_all` may be used to remove something not be a dataset lance::Error::NotFound { .. } => Error::TableNotFound { name: name.to_owned(), + source: Box::new(err), }, _ => Error::from(err), })?; diff --git a/rust/lancedb/src/error.rs b/rust/lancedb/src/error.rs index d5c0b52d..4312b386 100644 --- a/rust/lancedb/src/error.rs +++ b/rust/lancedb/src/error.rs @@ -6,6 +6,8 @@ use std::sync::PoisonError; use arrow_schema::ArrowError; use snafu::Snafu; +type BoxError = Box; + #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] pub enum Error { @@ -14,7 +16,7 @@ pub enum Error { #[snafu(display("Invalid input, {message}"))] InvalidInput { message: String }, #[snafu(display("Table '{name}' was not found"))] - TableNotFound { name: String }, + TableNotFound { name: String, source: BoxError }, #[snafu(display("Database '{name}' was not found"))] DatabaseNotFound { name: String }, #[snafu(display("Database '{name}' already exists."))] diff --git a/rust/lancedb/src/remote/db.rs b/rust/lancedb/src/remote/db.rs index 2f56a592..2f99ce4b 100644 --- a/rust/lancedb/src/remote/db.rs +++ b/rust/lancedb/src/remote/db.rs @@ -515,11 +515,8 @@ impl Database for RemoteDatabase { .client .post(&format!("/v1/table/{}/describe/", identifier)); let (request_id, rsp) = self.client.send_with_retry(req, None, true).await?; - if rsp.status() == StatusCode::NOT_FOUND { - return Err(crate::Error::TableNotFound { - name: identifier.clone(), - }); - } + let rsp = + RemoteTable::::handle_table_not_found(&request.name, rsp, &request_id).await?; let rsp = self.client.check_response(&request_id, rsp).await?; let version = parse_server_version(&request_id, &rsp)?; let table_identifier = build_table_identifier( diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index 9d8d3e55..0da9586d 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -336,16 +336,33 @@ impl RemoteTable { Ok(res) } + pub(super) async fn handle_table_not_found( + table_name: &str, + response: reqwest::Response, + request_id: &str, + ) -> Result { + let status = response.status(); + if status == StatusCode::NOT_FOUND { + let body = response.text().await.ok().unwrap_or_default(); + let request_error = Error::Http { + source: body.into(), + request_id: request_id.into(), + status_code: Some(status), + }; + return Err(Error::TableNotFound { + name: table_name.to_string(), + source: Box::new(request_error), + }); + } + Ok(response) + } + async fn check_table_response( &self, request_id: &str, response: reqwest::Response, ) -> Result { - if response.status() == StatusCode::NOT_FOUND { - return Err(Error::TableNotFound { - name: self.identifier.clone(), - }); - } + let response = Self::handle_table_not_found(&self.name, response, request_id).await?; self.client.check_response(request_id, response).await } @@ -681,8 +698,9 @@ impl BaseTable for RemoteTable { .map_err(|e| match e { // try to map the error to a more user-friendly error telling them // specifically that the version does not exist - Error::TableNotFound { name } => Error::TableNotFound { + Error::TableNotFound { name, source } => Error::TableNotFound { name: format!("{} (version: {})", name, version), + source, }, e => e, })?; @@ -1575,7 +1593,11 @@ mod tests { for result in results { let result = result.await; assert!(result.is_err()); - assert!(matches!(result, Err(Error::TableNotFound { name }) if name == "my_table")); + assert!( + matches!(&result, &Err(Error::TableNotFound { ref name, .. }) if name == "my_table") + ); + let full_error_report = snafu::Report::from_error(result.unwrap_err()).to_string(); + assert!(full_error_report.contains("table my_table not found")); } } @@ -2884,7 +2906,7 @@ mod tests { let res = table.checkout(43).await; println!("{:?}", res); assert!( - matches!(res, Err(Error::TableNotFound { name }) if name == "my_table (version: 43)") + matches!(res, Err(Error::TableNotFound { name, .. }) if name == "my_table (version: 43)") ); } diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index 65b95a4a..d44462ec 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -1534,6 +1534,7 @@ impl NativeTable { .map_err(|e| match e { lance::Error::DatasetNotFound { .. } => Error::TableNotFound { name: name.to_string(), + source: Box::new(e), }, source => Error::Lance { source }, })?; @@ -1554,6 +1555,7 @@ impl NativeTable { .file_stem() .ok_or(Error::TableNotFound { name: uri.to_string(), + source: format!("Could not extract table name from URI: '{}'", uri).into(), })? .to_str() .ok_or(Error::InvalidTableName {