This is an automated email from the ASF dual-hosted git repository.

Xuanwo pushed a commit to branch xuanwo/read-metadata-api
in repository https://gitbox.apache.org/repos/asf/opendal.git


The following commit(s) were added to refs/heads/xuanwo/read-metadata-api by 
this push:
     new a12d027b3 fix(object_store): fallback on range read metadata miss
a12d027b3 is described below

commit a12d027b3324cd55f52e8334dc538de3ef10e8dc
Author: Xuanwo <[email protected]>
AuthorDate: Wed May 27 19:37:03 2026 +0800

    fix(object_store): fallback on range read metadata miss
---
 core/services/s3/src/error.rs          |  9 +++++++++
 integrations/object_store/src/store.rs | 17 ++++++++++++++---
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/core/services/s3/src/error.rs b/core/services/s3/src/error.rs
index da675c6de..137851a88 100644
--- a/core/services/s3/src/error.rs
+++ b/core/services/s3/src/error.rs
@@ -131,6 +131,7 @@ pub fn parse_s3_error_code(code: &str) -> 
Option<(ErrorKind, bool)> {
         | "ExceedAccountRateLimit"
         | "ExceedBucketQPSLimit"
         | "ExceedBucketRateLimit" => Some((ErrorKind::RateLimited, true)),
+        "InvalidRange" => Some((ErrorKind::RangeNotSatisfied, false)),
         _ => None,
     }
 }
@@ -180,4 +181,12 @@ mod tests {
         let out: S3Error = de::from_reader(bs.reader()).expect("must success");
         assert_eq!(out, S3Error::default());
     }
+
+    #[test]
+    fn test_parse_s3_error_code_invalid_range() {
+        assert_eq!(
+            parse_s3_error_code("InvalidRange"),
+            Some((ErrorKind::RangeNotSatisfied, false))
+        );
+    }
 }
diff --git a/integrations/object_store/src/store.rs 
b/integrations/object_store/src/store.rs
index fb274c954..408adbd61 100644
--- a/integrations/object_store/src/store.rs
+++ b/integrations/object_store/src/store.rs
@@ -115,6 +115,17 @@ fn format_read_range(range: Option<&GetRange>, size: u64) 
-> Range<u64> {
     }
 }
 
+fn format_without_stat_error(err: opendal::Error, path: &str) -> 
object_store::Error {
+    match err.kind() {
+        opendal::ErrorKind::Unsupported | 
opendal::ErrorKind::RangeNotSatisfied => {
+            object_store::Error::NotSupported {
+                source: Box::new(err),
+            }
+        }
+        _ => format_object_store_error(err, path),
+    }
+}
+
 /// OpendalStore implements ObjectStore trait by using opendal.
 ///
 /// This allows users to use opendal as an object store without extra cost.
@@ -193,7 +204,7 @@ impl OpendalStore {
             .reader_options(raw_location, format_reader_options(options, None))
             .into_send()
             .await
-            .map_err(|err| format_object_store_error(err, location.as_ref()))?;
+            .map_err(|err| format_without_stat_error(err, location.as_ref()))?;
 
         let mut stream = match options.range.as_ref() {
             Some(GetRange::Bounded(range)) => {
@@ -206,13 +217,13 @@ impl OpendalStore {
             Some(GetRange::Suffix(_)) => unreachable!("suffix range needs 
object metadata"),
             None => reader.into_bytes_stream(..).into_send().await,
         }
-        .map_err(|err| format_object_store_error(err, location.as_ref()))?;
+        .map_err(|err| format_without_stat_error(err, location.as_ref()))?;
 
         let metadata = stream
             .metadata()
             .into_send()
             .await
-            .map_err(|err| format_object_store_error(err, location.as_ref()))?;
+            .map_err(|err| format_without_stat_error(err, location.as_ref()))?;
         let attributes = format_object_attributes(&metadata);
         let meta = format_object_meta(location.as_ref(), &metadata);
         let read_range = format_read_range(options.range.as_ref(), meta.size);

Reply via email to