mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-18 12:08:35 +00:00
fix: explain unsupported object storage mounts (#3823)
## Summary - classify unsupported local-filesystem operations from Lance as a NotSupported error - explain that object-storage mounts cannot provide the safe commit operations Lance requires and direct users to native object-store URIs - preserve existing error behavior for other local I/O failures and non-local backends ## Root cause Mountpoint for Amazon S3 exposes an S3 bucket as a local path but does not implement atomic rename. Lance uses atomic rename for safe local commits, and the resulting unsupported I/O error was previously passed through as a generic Lance error, leaving Python users with an opaque low-level failure. Transparent support for such mounts is not safe; direct s3:// access remains the supported path. ## Validation - cargo test --quiet --features remote -p lancedb error::tests - cargo test --quiet --features remote -p lancedb --lib (807 passed, 1 ignored) - cargo check --quiet --features remote --tests --examples - cargo clippy --quiet --features remote --tests --examples - cargo fmt --all -- --check Fixes #2016 <!-- lance-gatekeeper-fix:v1 agent=d53283c18fdb00a3a1b69448b1f40529 generation=1 --> --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
2ba7407dc3
commit
11f24b1df4
@@ -169,6 +169,12 @@ impl From<DataFusionError> for Error {
|
||||
|
||||
impl From<lance::Error> for Error {
|
||||
fn from(source: lance::Error) -> Self {
|
||||
if has_unsupported_local_filesystem_source(&source) {
|
||||
return Self::NotSupported {
|
||||
message: "the filesystem does not support an operation required for safe Lance commits (such as atomic rename). Object-storage mounts such as Mountpoint for Amazon S3 are not supported; use the native object-store URI (for example, s3://bucket/path) instead".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
// Try to unwrap external errors that were wrapped by lance
|
||||
match source {
|
||||
lance::Error::Wrapped { error, .. } => Self::from_box_error(error),
|
||||
@@ -181,6 +187,27 @@ impl From<lance::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
fn has_unsupported_local_filesystem_source(error: &(dyn std::error::Error + 'static)) -> bool {
|
||||
let mut current = Some(error);
|
||||
let mut is_local_filesystem = false;
|
||||
let mut is_unsupported = false;
|
||||
while let Some(error) = current {
|
||||
is_local_filesystem |= error
|
||||
.downcast_ref::<object_store::Error>()
|
||||
.is_some_and(|error| {
|
||||
matches!(error, object_store::Error::Generic { store, .. } if *store == "LocalFileSystem")
|
||||
});
|
||||
is_unsupported |= error
|
||||
.downcast_ref::<std::io::Error>()
|
||||
.is_some_and(|error| error.kind() == std::io::ErrorKind::Unsupported);
|
||||
if is_local_filesystem && is_unsupported {
|
||||
return true;
|
||||
}
|
||||
current = error.source();
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn from_box_error(mut source: Box<dyn std::error::Error + Send + Sync>) -> Self {
|
||||
source = match source.downcast::<Self>() {
|
||||
@@ -270,3 +297,46 @@ impl From<candle_core::Error> for Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unsupported_filesystem_operations_have_actionable_error() {
|
||||
let object_store_error = object_store::Error::Generic {
|
||||
store: "LocalFileSystem",
|
||||
source: Box::new(std::io::Error::from(std::io::ErrorKind::Unsupported)),
|
||||
};
|
||||
let lance_error = lance::Error::io_source(Box::new(object_store_error));
|
||||
|
||||
let error = Error::from(lance_error);
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
Error::NotSupported { message }
|
||||
if message.contains("Mountpoint for Amazon S3")
|
||||
&& message.contains("s3://bucket/path")
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_io_errors_remain_lance_errors() {
|
||||
let object_store_error = object_store::Error::Generic {
|
||||
store: "LocalFileSystem",
|
||||
source: Box::new(std::io::Error::from(std::io::ErrorKind::PermissionDenied)),
|
||||
};
|
||||
let lance_error = lance::Error::io_source(Box::new(object_store_error));
|
||||
|
||||
assert!(matches!(Error::from(lance_error), Error::Lance { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_non_filesystem_errors_remain_lance_errors() {
|
||||
let lance_error = lance::Error::io_source(Box::new(std::io::Error::from(
|
||||
std::io::ErrorKind::Unsupported,
|
||||
)));
|
||||
|
||||
assert!(matches!(Error::from(lance_error), Error::Lance { .. }));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user