diff --git a/crates/ecstore/src/cluster/rpc/client.rs b/crates/ecstore/src/cluster/rpc/client.rs index d06caadbb..435cbf64c 100644 --- a/crates/ecstore/src/cluster/rpc/client.rs +++ b/crates/ecstore/src/cluster/rpc/client.rs @@ -678,7 +678,9 @@ mod tests { fn embedded_tonic_status_is_recovered_across_error_conversions() { // DiskError and StorageError share one wrapper, so a status keeps its // typed classification whichever error it was converted into first. - let from_storage: DiskErrorType = crate::error::Error::from(tonic::Status::unavailable("peer gone")).into(); + let from_storage: DiskErrorType = crate::error::Error::from(tonic::Status::unavailable("peer gone")) + .narrow_to_disk() + .expect("status-derived Io errors narrow through the bridge"); let DiskError::Io(io_err) = &from_storage else { panic!("status-derived disk error should stay an Io error"); }; diff --git a/crates/ecstore/src/cluster/rpc/peer_rest_client.rs b/crates/ecstore/src/cluster/rpc/peer_rest_client.rs index a655ed595..a3ebe48aa 100644 --- a/crates/ecstore/src/cluster/rpc/peer_rest_client.rs +++ b/crates/ecstore/src/cluster/rpc/peer_rest_client.rs @@ -3593,13 +3593,23 @@ mod tests { // reduce_errs groups Io errors by kind plus rendered message: peers failing the // same operation must stay a single dominant error instead of one bucket per peer. let per_peer_errs = (0..4) - .map(|_| Some(DiskError::from(peer_failure_without_details("load_bucket_metadata", Some("shared"))))) + .map(|_| { + Some( + peer_failure_without_details("load_bucket_metadata", Some("shared")) + .narrow_to_disk() + .unwrap_or_else(DiskError::other), + ) + }) .collect::>(); let (count, dominant) = reduce_errs(&per_peer_errs, &[]); assert_eq!(count, 4, "one shared failure must not split into per-peer buckets"); assert_eq!( dominant, - Some(DiskError::from(peer_failure_without_details("load_bucket_metadata", Some("shared")))) + Some( + peer_failure_without_details("load_bucket_metadata", Some("shared")) + .narrow_to_disk() + .unwrap_or_else(DiskError::other) + ) ); assert_ne!( diff --git a/crates/ecstore/src/cluster/rpc/peer_s3_client.rs b/crates/ecstore/src/cluster/rpc/peer_s3_client.rs index 840cc5d28..cbd944fb0 100644 --- a/crates/ecstore/src/cluster/rpc/peer_s3_client.rs +++ b/crates/ecstore/src/cluster/rpc/peer_s3_client.rs @@ -775,9 +775,10 @@ fn try_acquire_bucket_heal_movement_guard<'a>( }; // Do not queue a receiver behind a movement writer while its coordinator // holds another node's read guard; failing fast breaks that cross-node cycle. - gate.try_read() - .map(Some) - .map_err(|_| crate::error::StorageError::SlowDown.into()) + // `StorageError::SlowDown` has no disk-layer identity of its own: it + // collapses to `TooManyOpenFiles` at this boundary (see `narrow_to_disk`). + // Constructed directly so the loss stays explicit at the site. + gate.try_read().map(Some).map_err(|_| Error::TooManyOpenFiles) } async fn acquire_bucket_heal_write_guard<'a>( @@ -787,7 +788,9 @@ async fn acquire_bucket_heal_write_guard<'a>( return Ok(None); }; let guard = gate.lock().await; - guard.ensure_write_safe("bucket heal cannot run while pool metadata requires recovery")?; + guard + .ensure_write_safe("bucket heal cannot run while pool metadata requires recovery") + .map_err(|e| e.narrow_to_disk().unwrap_or_else(Error::other))?; Ok(Some(guard)) } @@ -2366,7 +2369,11 @@ mod tests { let err = try_acquire_bucket_heal_movement_guard(Some(&gate), false) .expect_err("receiver must not wait behind a queued movement writer"); - assert_eq!(err, crate::error::StorageError::SlowDown.into()); + assert_eq!( + err, + Error::TooManyOpenFiles, + "SlowDown collapses to TooManyOpenFiles at the disk boundary" + ); assert!( try_acquire_bucket_heal_movement_guard(Some(&gate), true) .expect("coordinator-owned movement guard should be reused") diff --git a/crates/ecstore/src/disk/error.rs b/crates/ecstore/src/disk/error.rs index 326e38952..6f771f790 100644 --- a/crates/ecstore/src/disk/error.rs +++ b/crates/ecstore/src/disk/error.rs @@ -351,7 +351,7 @@ impl From for DiskError { // classification instead of degrading to `DiskError::Io`, which // quorum aggregation (`reduce_errs`) would count as a distinct error. Err(io_error) => match io_error.downcast::() { - Ok(storage_error) => storage_error.into(), + Ok(storage_error) => storage_error.narrow_to_disk().unwrap_or_else(DiskError::other), Err(io_error) => DiskError::Io(io_error), }, } @@ -1189,7 +1189,7 @@ mod tests { &storage, crate::error::StorageError::RemoteClientUnavailable(detail) if detail == "handshake timed out" )); - let narrowed: DiskError = storage.into(); + let narrowed: DiskError = storage.narrow_to_disk().expect("typed variant must narrow"); assert_eq!(narrowed, original); assert!(matches!( &narrowed, diff --git a/crates/ecstore/src/disk/local.rs b/crates/ecstore/src/disk/local.rs index e6fe0cd26..3d591a06d 100644 --- a/crates/ecstore/src/disk/local.rs +++ b/crates/ecstore/src/disk/local.rs @@ -5096,7 +5096,7 @@ impl LocalDisk { ensure_data_usage_layout(&io_root) .await - .map_err(DiskError::from) + .map_err(|e| e.narrow_to_disk().unwrap_or_else(DiskError::other)) .inspect_err(|err| { log_startup_disk_error("ensure_data_usage_layout", &root, err); })?; diff --git a/crates/ecstore/src/error/conversion_roundtrip_tests.rs b/crates/ecstore/src/error/conversion_roundtrip_tests.rs index fd7c606b4..6602ad76f 100644 --- a/crates/ecstore/src/error/conversion_roundtrip_tests.rs +++ b/crates/ecstore/src/error/conversion_roundtrip_tests.rs @@ -49,7 +49,7 @@ fn heal_matched_disk_variants() -> Vec { fn disk_to_storage_to_disk_preserves_heal_matched_variants() { for disk_err in heal_matched_disk_variants() { let storage: StorageError = disk_err.clone().into(); - let back: DiskError = storage.into(); + let back: DiskError = storage.narrow_to_disk().expect("heal-matched variants must narrow"); assert_eq!(back, disk_err, "DiskError → StorageError → DiskError must be identity for {disk_err:?}"); } } @@ -63,7 +63,10 @@ fn storage_to_disk_to_storage_preserves_heal_matched_variants() { StorageError::ErasureWriteQuorum, ]; for storage_err in variants { - let disk: DiskError = storage_err.clone().into(); + let disk: DiskError = storage_err + .clone() + .narrow_to_disk() + .expect("heal-matched variants must narrow"); let back: StorageError = disk.into(); assert_eq!( back, storage_err, @@ -77,11 +80,11 @@ fn storage_to_disk_to_storage_preserves_heal_matched_variants() { /// A classifier on the far side of the disk boundary can no longer tell /// backpressure ("please slow down") apart from fd exhaustion. /// -/// Pinned as-is for backlog#1845; PR4's fallible `narrow_to_disk()` is the -/// planned place to surface this loss explicitly. +/// The fallible `narrow_to_disk()` keeps this collapse as a documented arm; +/// only variants with no disk-layer identity at all narrow to `Err`. #[test] fn slowdown_collapses_to_too_many_open_files_across_disk_boundary() { - let disk: DiskError = StorageError::SlowDown.into(); + let disk: DiskError = StorageError::SlowDown.narrow_to_disk().expect("SlowDown narrows, lossily"); assert_eq!(disk, DiskError::TooManyOpenFiles); let back: StorageError = disk.into(); @@ -96,7 +99,9 @@ fn slowdown_collapses_to_too_many_open_files_across_disk_boundary() { /// and comes back as `DiskFull`. #[test] fn storage_full_collapses_to_disk_full_across_disk_boundary() { - let disk: DiskError = StorageError::StorageFull.into(); + let disk: DiskError = StorageError::StorageFull + .narrow_to_disk() + .expect("StorageFull narrows, lossily"); assert_eq!(disk, DiskError::DiskFull); let back: StorageError = disk.into(); @@ -204,7 +209,10 @@ fn storage_to_filemeta_to_storage_preserves_matched_variants() { StorageError::Unexpected, ]; for storage_err in variants { - let filemeta: rustfs_filemeta::Error = storage_err.clone().into(); + let filemeta: rustfs_filemeta::Error = storage_err + .clone() + .narrow_to_filemeta() + .expect("matched variants must narrow"); let back: StorageError = filemeta.into(); assert_eq!( back, storage_err, @@ -219,7 +227,13 @@ fn storage_to_filemeta_to_storage_preserves_matched_variants() { /// recovers identity" property of the by-design bridge stays load-bearing. #[test] fn storage_to_filemeta_other_recovers_identity_via_io_bridge() { - let filemeta: rustfs_filemeta::Error = StorageError::SlowDown.into(); + // A variant with no filemeta identity narrows to Err; callers that need a + // total conversion fold it into the io-backed other(), and the identity + // bridge recovers the boxed StorageError on the way back. + let refused = StorageError::SlowDown + .narrow_to_filemeta() + .expect_err("SlowDown has no filemeta identity"); + let filemeta = rustfs_filemeta::Error::other(refused); assert!( matches!(filemeta, rustfs_filemeta::Error::Io(_)), "unmatched variants fold into the io-backed other()" diff --git a/crates/ecstore/src/error/mod.rs b/crates/ecstore/src/error/mod.rs index 4845efa3e..a7e78d972 100644 --- a/crates/ecstore/src/error/mod.rs +++ b/crates/ecstore/src/error/mod.rs @@ -327,9 +327,20 @@ impl From for StorageError { } } -impl From for DiskError { - fn from(val: StorageError) -> Self { - match val { +impl StorageError { + /// Narrow a store-layer error to the disk-layer vocabulary. + /// + /// This used to be a blanket `impl From for DiskError`, which + /// let `?` silently push store-only errors across the disk boundary into + /// `DiskError::other`, fragmenting `reduce_errs` quorum buckets + /// (backlog#1845). Narrowing is now a named, fallible operation: variants + /// with a disk-layer identity map across (including two documented lossy + /// collapses kept for compatibility — `SlowDown` → `TooManyOpenFiles` and + /// `StorageFull` → `DiskFull`, pinned by conversion_roundtrip_tests), and + /// everything else comes back as `Err(self)` so the caller decides what + /// crossing the boundary means for it. + pub fn narrow_to_disk(self) -> core::result::Result { + Ok(match self { StorageError::Io(io_error) => io_error.into(), StorageError::Unexpected => DiskError::Unexpected, StorageError::FileNotFound => DiskError::FileNotFound, @@ -375,8 +386,26 @@ impl From for DiskError { StorageError::VolumeAccessDenied => DiskError::VolumeAccessDenied, StorageError::FileAccessDenied => DiskError::FileAccessDenied, StorageError::RemoteClientUnavailable(detail) => DiskError::RemoteClientUnavailable(detail), - _ => DiskError::other(val), - } + val => return Err(val), + }) + } + + /// Same contract as [`StorageError::narrow_to_disk`], for the + /// `rustfs_filemeta::Error` vocabulary (formerly a blanket `From` impl + /// with an `other()` catch-all). No production path currently narrows in + /// this direction; the named form keeps future callers deliberate. + pub fn narrow_to_filemeta(self) -> core::result::Result { + Ok(match self { + StorageError::Unexpected => rustfs_filemeta::Error::Unexpected, + StorageError::FileNotFound => rustfs_filemeta::Error::FileNotFound, + StorageError::FileVersionNotFound => rustfs_filemeta::Error::FileVersionNotFound, + StorageError::FileCorrupt => rustfs_filemeta::Error::FileCorrupt, + StorageError::DoneForNow => rustfs_filemeta::Error::DoneForNow, + StorageError::MethodNotAllowed => rustfs_filemeta::Error::MethodNotAllowed, + StorageError::VolumeNotFound => rustfs_filemeta::Error::VolumeNotFound, + StorageError::Io(io_error) => io_error.into(), + val => return Err(val), + }) } } @@ -433,22 +462,6 @@ impl From for StorageError { } } -impl From for rustfs_filemeta::Error { - fn from(val: StorageError) -> Self { - match val { - StorageError::Unexpected => rustfs_filemeta::Error::Unexpected, - StorageError::FileNotFound => rustfs_filemeta::Error::FileNotFound, - StorageError::FileVersionNotFound => rustfs_filemeta::Error::FileVersionNotFound, - StorageError::FileCorrupt => rustfs_filemeta::Error::FileCorrupt, - StorageError::DoneForNow => rustfs_filemeta::Error::DoneForNow, - StorageError::MethodNotAllowed => rustfs_filemeta::Error::MethodNotAllowed, - StorageError::VolumeNotFound => rustfs_filemeta::Error::VolumeNotFound, - StorageError::Io(io_error) => io_error.into(), - _ => rustfs_filemeta::Error::other(val), - } - } -} - impl PartialEq for StorageError { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -1438,7 +1451,9 @@ mod tests { for original in all_variants { let storage_error: StorageError = original.clone().into(); - let round_tripped: DiskError = storage_error.into(); + let round_tripped: DiskError = storage_error + .narrow_to_disk() + .expect("every disk-representable StorageError must narrow back"); assert_eq!( std::mem::discriminant(&original), @@ -1452,7 +1467,7 @@ mod tests { // message must both survive the round trip. let io_original = DiskError::Io(IoError::new(ErrorKind::PermissionDenied, "denied")); let storage_error: StorageError = io_original.clone().into(); - let io_round_tripped: DiskError = storage_error.into(); + let io_round_tripped: DiskError = storage_error.narrow_to_disk().expect("Io narrows through the bridge"); assert_eq!(io_original, io_round_tripped); match io_round_tripped { DiskError::Io(inner) => { @@ -1700,8 +1715,13 @@ mod tests { assert_eq!(converted_storage_error, expected_storage_error); // Test reverse conversion - let converted_back: rustfs_filemeta::Error = converted_storage_error.into(); - assert_eq!(converted_back, expected_storage_error.into()); + let converted_back: rustfs_filemeta::Error = converted_storage_error + .narrow_to_filemeta() + .expect("matched variants must narrow to filemeta"); + let expected_back: rustfs_filemeta::Error = expected_storage_error + .narrow_to_filemeta() + .expect("matched variants must narrow to filemeta"); + assert_eq!(converted_back, expected_back); } } diff --git a/crates/ecstore/src/set_disk/ops/heal.rs b/crates/ecstore/src/set_disk/ops/heal.rs index 62ada21f1..adcea3a60 100644 --- a/crates/ecstore/src/set_disk/ops/heal.rs +++ b/crates/ecstore/src/set_disk/ops/heal.rs @@ -474,13 +474,15 @@ impl SetDisks { // Bound, not `_`: this guard must live to the end of the scope. A bare // `_` would drop it here and release the namespace write lock. let _write_lock_guard = if !opts.no_lock { - let ns_lock = self.new_ns_lock(bucket, object).await?; - Some( - ns_lock - .get_write_lock(get_lock_acquire_timeout()) - .await - .map_err(|e| self.map_namespace_lock_error(bucket, object, "write", e))?, - ) + let ns_lock = self + .new_ns_lock(bucket, object) + .await + .map_err(|e| e.narrow_to_disk().unwrap_or_else(DiskError::other))?; + Some(ns_lock.get_write_lock(get_lock_acquire_timeout()).await.map_err(|e| { + self.map_namespace_lock_error(bucket, object, "write", e) + .narrow_to_disk() + .unwrap_or_else(DiskError::other) + })?) } else { None }; @@ -2200,13 +2202,15 @@ impl crate::storage_api_contracts::heal::HealOperations for SetDisks { opts: &HealOpts, ) -> Result<(HealResultItem, Option)> { let _write_lock_guard = if !opts.no_lock { - let ns_lock = self.new_ns_lock(bucket, object).await?; - Some( - ns_lock - .get_write_lock(get_lock_acquire_timeout()) - .await - .map_err(|e| self.map_namespace_lock_error(bucket, object, "write", e))?, - ) + let ns_lock = self + .new_ns_lock(bucket, object) + .await + .map_err(|e| e.narrow_to_disk().unwrap_or_else(DiskError::other))?; + Some(ns_lock.get_write_lock(get_lock_acquire_timeout()).await.map_err(|e| { + self.map_namespace_lock_error(bucket, object, "write", e) + .narrow_to_disk() + .unwrap_or_else(DiskError::other) + })?) } else { None }; @@ -2306,13 +2310,15 @@ impl crate::storage_api_contracts::heal::HealOperations for SetDisks { async fn check_abandoned_parts(&self, bucket: &str, object: &str, opts: &HealOpts) -> Result<()> { let started_at = std::time::Instant::now(); let _write_lock_guard = if !opts.no_lock { - let ns_lock = self.new_ns_lock(bucket, object).await?; - Some( - ns_lock - .get_write_lock(get_lock_acquire_timeout()) - .await - .map_err(|e| self.map_namespace_lock_error(bucket, object, "write", e))?, - ) + let ns_lock = self + .new_ns_lock(bucket, object) + .await + .map_err(|e| e.narrow_to_disk().unwrap_or_else(DiskError::other))?; + Some(ns_lock.get_write_lock(get_lock_acquire_timeout()).await.map_err(|e| { + self.map_namespace_lock_error(bucket, object, "write", e) + .narrow_to_disk() + .unwrap_or_else(DiskError::other) + })?) } else { None }; diff --git a/crates/ecstore/src/set_disk/ops/object.rs b/crates/ecstore/src/set_disk/ops/object.rs index 30a1e30a4..2de84eaae 100644 --- a/crates/ecstore/src/set_disk/ops/object.rs +++ b/crates/ecstore/src/set_disk/ops/object.rs @@ -2205,7 +2205,7 @@ impl SetDisks { } let current = read_object_transaction_epoch_fence(self, bucket, object) .await - .map_err(DiskError::from)?; + .map_err(|e| e.narrow_to_disk().unwrap_or_else(DiskError::other))?; let disks = self.get_disks_internal().await; let mut removed = 0usize; diff --git a/crates/ecstore/src/store/heal.rs b/crates/ecstore/src/store/heal.rs index da18546eb..5eed710c6 100644 --- a/crates/ecstore/src/store/heal.rs +++ b/crates/ecstore/src/store/heal.rs @@ -659,7 +659,7 @@ mod tests { let _movement_guard = self .movement_gate .try_read() - .map_err(|_| crate::error::StorageError::SlowDown)?; + .map_err(|_| crate::disk::error::DiskError::TooManyOpenFiles)?; Ok(HealResultItem::default()) } diff --git a/crates/ecstore/src/store/list_objects.rs b/crates/ecstore/src/store/list_objects.rs index 5b18481d7..683e9eeb3 100644 --- a/crates/ecstore/src/store/list_objects.rs +++ b/crates/ecstore/src/store/list_objects.rs @@ -12,6 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// Narrow a store error into the filemeta vocabulary for `MetaCacheEntriesSortedResult.err`. +/// Faithful port of the retired blanket `From for rustfs_filemeta::Error`: +/// unmatched variants fold into the io-backed `other()`, whose boxed identity the io bridge +/// recovers on the way back (see conversion_roundtrip_tests). +fn to_filemeta_err(err: Error) -> rustfs_filemeta::Error { + err.narrow_to_filemeta().unwrap_or_else(rustfs_filemeta::Error::other) +} + use crate::bucket::metadata_sys::{get_versioning_config, has_authoritative_never_versioned_state}; use crate::bucket::utils::check_list_objs_args; use crate::bucket::versioning::VersioningApi; @@ -3162,7 +3170,7 @@ impl ECStore { .list_path(&page_opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let reached_end = match list_result.err.take() { @@ -3525,7 +3533,7 @@ impl ECStore { .list_path(&provider_opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); @@ -3788,7 +3796,7 @@ impl ECStore { .list_path(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -3928,7 +3936,7 @@ impl ECStore { .list_path(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -4109,7 +4117,7 @@ impl ECStore { match res{ Ok(err) => { log_list_path_worker_error("store", "worker_error", &log_context, err.as_ref()); - MetaCacheEntriesSortedResult{ entries: None, err: Some(err.as_ref().clone().into()) } + MetaCacheEntriesSortedResult{ entries: None, err: Some(to_filemeta_err(err.as_ref().clone())) } }, Err(err) => { log_list_path_worker_error("store", "error_channel_closed", &log_context, &err); @@ -4129,7 +4137,7 @@ impl ECStore { if let Ok(err) = err_rx.try_recv() { log_list_path_worker_error("store", "trailing_worker_error", &log_context, err.as_ref()); - result.err = Some(err.as_ref().clone().into()); + result.err = Some(to_filemeta_err(err.as_ref().clone())); } if result.err.is_some() { @@ -4150,7 +4158,7 @@ impl ECStore { } if !truncated { - result.err = Some(Error::Unexpected.into()); + result.err = Some(rustfs_filemeta::Error::Unexpected); } } @@ -4350,7 +4358,7 @@ impl ECStore { let reader_disks = disks.len(); let path = base_dir_from_prefix(prefix); - ensure_non_empty_listing_disks(bucket, &path, &disks)?; + ensure_non_empty_listing_disks(bucket, &path, &disks).map_err(to_filemeta_err)?; let mut filter_prefix = { prefix @@ -4816,7 +4824,7 @@ async fn gather_results( o: MetaCacheEntries(entries), ..Default::default() }), - err: Some(Error::Unexpected.into()), + err: Some(rustfs_filemeta::Error::Unexpected), }) .await .is_err() @@ -5133,7 +5141,7 @@ impl Sets { .list_path(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -5222,7 +5230,7 @@ impl Sets { .list_path(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -5383,7 +5391,7 @@ impl Sets { match res { Ok(err) => { log_list_path_worker_error("sets", "worker_error", &log_context, err.as_ref()); - MetaCacheEntriesSortedResult { entries: None, err: Some(err.as_ref().clone().into()) } + MetaCacheEntriesSortedResult { entries: None, err: Some(to_filemeta_err(err.as_ref().clone())) } }, Err(err) => { log_list_path_worker_error("sets", "error_channel_closed", &log_context, &err); @@ -5398,7 +5406,7 @@ impl Sets { if let Ok(err) = err_rx.try_recv() { log_list_path_worker_error("sets", "trailing_worker_error", &log_context, err.as_ref()); - result.err = Some(err.as_ref().clone().into()); + result.err = Some(to_filemeta_err(err.as_ref().clone())); } if result.err.is_some() { @@ -5419,7 +5427,7 @@ impl Sets { } if !truncated { - result.err = Some(Error::Unexpected.into()); + result.err = Some(rustfs_filemeta::Error::Unexpected); } } @@ -5584,7 +5592,7 @@ impl Sets { let reader_disks = disks.len(); let path = base_dir_from_prefix(prefix); - ensure_non_empty_listing_disks(bucket, &path, &disks)?; + ensure_non_empty_listing_disks(bucket, &path, &disks).map_err(to_filemeta_err)?; let mut filter_prefix = prefix .trim_start_matches(&path) @@ -5911,7 +5919,7 @@ impl SetDisks { .list_path_result(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -6040,7 +6048,7 @@ impl SetDisks { .list_path_result(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -6128,7 +6136,7 @@ impl SetDisks { .list_path_result(&opts) .await .unwrap_or_else(|err| MetaCacheEntriesSortedResult { - err: Some(err.into()), + err: Some(to_filemeta_err(err)), ..Default::default() }); let next_cache_id = list_result.entries.as_ref().and_then(|entries| entries.list_id.clone()); @@ -6436,7 +6444,7 @@ impl SetDisks { match res { Ok(err) => { log_list_path_worker_error("set_disks", "worker_error", &log_context, err.as_ref()); - MetaCacheEntriesSortedResult { entries: None, err: Some(err.as_ref().clone().into()) } + MetaCacheEntriesSortedResult { entries: None, err: Some(to_filemeta_err(err.as_ref().clone())) } }, Err(err) => { log_list_path_worker_error("set_disks", "error_channel_closed", &log_context, &err); @@ -6451,7 +6459,7 @@ impl SetDisks { if let Ok(err) = err_rx.try_recv() { log_list_path_worker_error("set_disks", "trailing_worker_error", &log_context, err.as_ref()); - result.err = Some(err.as_ref().clone().into()); + result.err = Some(to_filemeta_err(err.as_ref().clone())); } if result.err.is_some() { @@ -6472,7 +6480,7 @@ impl SetDisks { } if !truncated { - result.err = Some(Error::Unexpected.into()); + result.err = Some(rustfs_filemeta::Error::Unexpected); } }