Repository: heremaps/flatdata (language: python)

Issue:
flatdata does not report errors in optional sub-archives
Optional sub-archives in flatdata are not propagating serious errors when being opened. Instead serious errors are treated the same was as missing data.
flatdata does not report errors in optional sub-archives
Optional sub-archives in flatdata are not propagating serious errors when being opened. Instead serious errors are treated the same was as missing data.


Discussion / hints:

Fix patch:
diff --git a/flatdata-generator/flatdata/generator/templates/rust/archive.jinja2 b/flatdata-generator/flatdata/generator/templates/rust/archive.jinja2
index 91b96392..a3867cbb 100644
--- a/flatdata-generator/flatdata/generator/templates/rust/archive.jinja2
+++ b/flatdata-generator/flatdata/generator/templates/rust/archive.jinja2
@@ -114,35 +114,31 @@ impl {{archive.name}} {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("{{archive.name}}"), schema::{{ archive_ns }}::{{ archive.name | camel_to_snake_case | upper }})?;
 
         {% for r in archive.resources | supported_resources %}
+        let {{r.name}} = {
+            use flatdata::{% if r.optional %}check_optional_resource{% else %}check_resource{% endif %} as check;
+            let max_size = {% if r.max_size %}Some({{ r.max_size }}){% else %}None{% endif %};
         {% if [r] | vector_resources %}
-        {% set t = fully_qualified_name(archive, r.referenced_structures[0].node) %}
-        let resource = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
-        let {{r.name}} = resource.map(|x| <&[{{t}}]>::from_bytes(x)){% if r.optional %}.ok().transpose()?{% else %}??{% endif %};
-        {% if r.max_size %}
-        let size = {% if r.optional %}{{ r.name }}.as_ref().map(|r| r.len()).unwrap_or(0){% else %}{{ r.name }}.len(){% endif %};
-        if size > {{ r.max_size }} { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "{{ r.name }}", size}); }
-        {% endif %}
+            {% set t = fully_qualified_name(archive, r.referenced_structures[0].node) %}
+            let resource = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
+            check("{{ r.name }}", |r| r.len(), max_size, resource.and_then(|x| <&[{{t}}]>::from_bytes(x)))?
         {% elif [r] | instance_resources %}
-        {% set t = fully_qualified_name(archive, r.referenced_structures[0].node) %}
-        let resource = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
-        let {{r.name}} = resource.map(|x| {{t}}::from_bytes_slice(x)){% if r.optional %}.ok().transpose()?{% else %}??{% endif %};
+            {% set t = fully_qualified_name(archive, r.referenced_structures[0].node) %}
+            let resource = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
+            check("{{ r.name }}", |_| 0, max_size, resource.and_then(|x| {{t}}::from_bytes_slice(x)))?
         {% elif [r] | rawdata_resources %}
-        let resource = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
-        let {{r.name}} = resource.map(|x| flatdata::RawData::new(x)){% if r.optional %}.ok(){% else %}?{% endif %};
-        {% if r.max_size %}
-        let size = {% if r.optional %}{{ r.name }}.as_ref().map(|r| r.len()).unwrap_or(0){% else %}{{ r.name }}.len(){% endif %};
-        if size > {{ r.max_size }} { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "{{ r.name }}", size}); }
-        {% endif %}
+            let resource = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
+            check("{{ r.name }}", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
         {% elif [r] | multivector_resources %}
-        {% set i = fully_qualified_name(archive, r.index_reference.node) %}
-        let {{r.name}} = {
+            {% set i = fully_qualified_name(archive, r.index_reference.node) %}
             let index_schema = &format!("index({})", schema::{{ archive_ns }}::resources::{{ r.name | upper }});
             let index = extend(storage.read("{{r.name}}_index", &index_schema));
             let data = extend(storage.read("{{r.name}}", schema::{{ archive_ns }}::resources::{{ r.name | upper }}));
@@ -153,23 +149,19 @@ impl {{archive.name}} {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            {% if r.optional %}
-            result.ok()
-            {% else %}
-            result?
-            {% endif %}
-        };
-        {% if r.max_size %}
-        let size = {% if r.optional %}{{ r.name }}.as_ref().map(|r| r.len()).unwrap_or(0){% else %}{{ r.name }}.len(){% endif %};
-        if size > {{ r.max_size }} { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "{{ r.name }}", size}); }
-        {% endif %}
+            check("{{ r.name }}", |r| r.len(), max_size, result)?
         {% elif [r] | subarchive_resources %}
-        {% set t = fully_qualified_name(archive, r.target.node) %}
-        let {{r.name}} = {{t}}::open(storage.subdir("{{r.name}}")){% if r.optional %}.ok(){% else %}?{% endif %};
+            {% set t = fully_qualified_name(archive, r.target.node) %}
+            check("{{ r.name }}", |_| 0, max_size, {{t}}::open(storage.subdir("{{r.name}}")))?
         {% endif %}
+        };
         {% endfor %}
 
         Ok(Self {
diff --git a/flatdata-rs/lib/src/error.rs b/flatdata-rs/lib/src/error.rs
index f3b93b0d..a2b3af8c 100644
--- a/flatdata-rs/lib/src/error.rs
+++ b/flatdata-rs/lib/src/error.rs
@@ -42,6 +42,8 @@ pub enum ResourceStorageError {
         /// Size of the resource
         size: usize,
     },
+    /// A resource / archive is missing completely
+    Missing,
     #[doc(hidden)]
     __Nonexhaustive,
 }
@@ -71,6 +73,7 @@ impl error::Error for ResourceStorageError {
             ResourceStorageError::Utf8Error(_) => "utf8 error in schema",
             ResourceStorageError::WrongSignature { .. } => "schema is not matching expected schema",
             ResourceStorageError::TooBig { .. } => "resource is too big",
+            ResourceStorageError::Missing => "Missing resource / archive",
             ResourceStorageError::__Nonexhaustive => "n/a",
         }
     }
diff --git a/flatdata-rs/lib/src/lib.rs b/flatdata-rs/lib/src/lib.rs
index 4cb4b09b..3a7b4320 100644
--- a/flatdata-rs/lib/src/lib.rs
+++ b/flatdata-rs/lib/src/lib.rs
@@ -190,7 +190,8 @@ pub use crate::{
     multivector::MultiVector,
     rawdata::RawData,
     storage::{
-        create_archive, create_external_vector, create_multi_vector, ResourceStorage, StorageHandle,
+        check_optional_resource, check_resource, create_archive, create_external_vector,
+        create_multi_vector, ResourceStorage, StorageHandle,
     },
     structs::*,
     vector::*,
diff --git a/flatdata-rs/lib/src/storage.rs b/flatdata-rs/lib/src/storage.rs
index 90f83572..708e3cf3 100644
--- a/flatdata-rs/lib/src/storage.rs
+++ b/flatdata-rs/lib/src/storage.rs
@@ -94,9 +94,15 @@ pub trait ResourceStorage: std::fmt::Debug {
         resource_name: &str,
         expected_schema: &str,
     ) -> Result<&[u8], ResourceStorageError> {
-        let data = self
-            .read_resource(resource_name)
-            .map_err(|e| ResourceStorageError::from_io_error(e, resource_name.into()))?;
+        let data = self.read_resource(resource_name).map_err(|e| {
+            if e.kind() == std::io::ErrorKind::NotFound {
+                // resource is missing completely, this might not be an error,
+                // e.g. in case of optional resources / archives
+                ResourceStorageError::Missing
+            } else {
+                ResourceStorageError::from_io_error(e, resource_name.into())
+            }
+        })?;
 
         let schema_name = format!("{}.schema", resource_name);
         let schema = self
@@ -130,6 +136,58 @@ pub trait ResourceStorage: std::fmt::Debug {
 // Resource factory helpers
 //
 
+/// Helper that checks if an error was serious, or just a missing optional resource
+#[doc(hidden)]
+pub fn check_optional_resource<T>(
+    resource_name: &'static str,
+    size_fn: impl FnOnce(&T) -> usize,
+    max_size: Option<usize>,
+    x: Result<T, ResourceStorageError>,
+) -> Result<Option<T>, ResourceStorageError> {
+    match (x, max_size) {
+        (Err(ResourceStorageError::Missing), _) => Ok(None),
+        (Err(e), _) => Err(e),
+        (Ok(x), None) => Ok(Some(x)),
+        (Ok(x), Some(max_size)) => {
+            let size = size_fn(&x);
+            if size > max_size {
+                Err(ResourceStorageError::TooBig {
+                    size,
+                    resource_name,
+                })
+            } else {
+                Ok(Some(x))
+            }
+        }
+    }
+}
+
+/// Helper that checks if a resource was successfully opened
+#[doc(hidden)]
+pub fn check_resource<T>(
+    resource_name: &'static str,
+    size_fn: impl FnOnce(&T) -> usize,
+    max_size: Option<usize>,
+    x: Result<T, ResourceStorageError>,
+) -> Result<T, ResourceStorageError> {
+    match (x, max_size) {
+        (Err(ResourceStorageError::Missing), _) => Err(ResourceStorageError::MissingData),
+        (Err(e), _) => Err(e),
+        (Ok(x), None) => Ok(x),
+        (Ok(x), Some(max_size)) => {
+            let size = size_fn(&x);
+            if size > max_size {
+                Err(ResourceStorageError::TooBig {
+                    size,
+                    resource_name,
+                })
+            } else {
+                Ok(x)
+            }
+        }
+    }
+}
+
 /// Helper for creating an external vector in the given resource storage.
 ///
 /// Creates a new resource with given name and schema in storage, and returns


Test patch:
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/comments.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/comments.rs.1
index 08969413..05ce18a3 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/comments.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/comments.rs.1
@@ -32,14 +32,21 @@ impl Foo {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("Foo"), schema::foo::FOO)?;
 
-        let resource = extend(storage.read("bar", schema::foo::resources::BAR));
-        let bar = resource.map(|x| flatdata::RawData::new(x))?;
+        let bar = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("bar", schema::foo::resources::BAR));
+            check("bar", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
+
 
         Ok(Self {
             _storage: storage,
@@ -113,14 +120,21 @@ impl Bar {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("Bar"), schema::bar::BAR)?;
 
-        let resource = extend(storage.read("foo", schema::bar::resources::FOO));
-        let foo = resource.map(|x| flatdata::RawData::new(x))?;
+        let foo = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("foo", schema::bar::resources::FOO));
+            check("foo", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
+
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/empty.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/empty.rs.1
index b6c52c91..4d287267 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/empty.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/empty.rs.1
@@ -23,9 +23,11 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/multivector.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/multivector.rs.1
index 31c37a8e..26f0945c 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/multivector.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/multivector.rs.1
@@ -615,13 +615,17 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
         let data = {
+            use flatdata::check_resource as check;
+            let max_size = None;
             let index_schema = &format!("index({})", schema::a::resources::DATA);
             let index = extend(storage.read("data_index", &index_schema));
             let data = extend(storage.read("data", schema::a::resources::DATA));
@@ -632,12 +636,18 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result?
+            check("data", |r| r.len(), max_size, result)?
         };
         let optional_data = {
+            use flatdata::check_optional_resource as check;
+            let max_size = None;
             let index_schema = &format!("index({})", schema::a::resources::OPTIONAL_DATA);
             let index = extend(storage.read("optional_data_index", &index_schema));
             let data = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
@@ -648,12 +658,18 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result.ok()
+            check("optional_data", |r| r.len(), max_size, result)?
         };
         let data_u64_index = {
+            use flatdata::check_resource as check;
+            let max_size = None;
             let index_schema = &format!("index({})", schema::a::resources::DATA_U64_INDEX);
             let index = extend(storage.read("data_u64_index_index", &index_schema));
             let data = extend(storage.read("data_u64_index", schema::a::resources::DATA_U64_INDEX));
@@ -664,10 +680,14 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result?
+            check("data_u64_index", |r| r.len(), max_size, result)?
         };
 
         Ok(Self {
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/namespaces.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/namespaces.rs.1
index 9fa1f31b..0481f896 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/namespaces.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/namespaces.rs.1
@@ -151,14 +151,20 @@ impl X {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("X"), schema::x::X)?;
 
-        let resource = extend(storage.read("payload", schema::x::resources::PAYLOAD));
-        let payload = resource.map(|x| flatdata::RawData::new(x))?;
+        let payload = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("payload", schema::x::resources::PAYLOAD));
+            check("payload", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
@@ -376,14 +382,20 @@ impl X {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("X"), schema::x::X)?;
 
-        let resource = extend(storage.read("payload", schema::x::resources::PAYLOAD));
-        let payload = resource.map(|x| flatdata::RawData::new(x))?;
+        let payload = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("payload", schema::x::resources::PAYLOAD));
+            check("payload", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
@@ -606,17 +618,29 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let resource = extend(storage.read("single", schema::a::resources::SINGLE));
-        let single = resource.map(|x| super::n::S::from_bytes_slice(x))??;
-        let resource = extend(storage.read("list", schema::a::resources::LIST));
-        let list = resource.map(|x| <&[super::m::S]>::from_bytes(x))??;
+        let single = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("single", schema::a::resources::SINGLE));
+            check("single", |_| 0, max_size, resource.and_then(|x| super::n::S::from_bytes_slice(x)))?
+        };
+        let list = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("list", schema::a::resources::LIST));
+            check("list", |r| r.len(), max_size, resource.and_then(|x| <&[super::m::S]>::from_bytes(x)))?
+        };
         let multi = {
+            use flatdata::check_resource as check;
+            let max_size = None;
             let index_schema = &format!("index({})", schema::a::resources::MULTI);
             let index = extend(storage.read("multi_index", &index_schema));
             let data = extend(storage.read("multi", schema::a::resources::MULTI));
@@ -627,12 +651,20 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result?
+            check("multi", |r| r.len(), max_size, result)?
+        };
+        let inner = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            check("inner", |_| 0, max_size, super::n::X::open(storage.subdir("inner")))?
         };
-        let inner = super::n::X::open(storage.subdir("inner"))?;
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/ranges.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/ranges.rs.1
index 9a2cd885..c40868e8 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/ranges.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/ranges.rs.1
@@ -120,14 +120,20 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let resource = extend(storage.read("data", schema::a::resources::DATA));
-        let data = resource.map(|x| <&[super::n::S]>::from_bytes(x))??;
+        let data = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::a::resources::DATA));
+            check("data", |r| r.len(), max_size, resource.and_then(|x| <&[super::n::S]>::from_bytes(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/raw_data.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/raw_data.rs.1
index 03e34c52..c71afbdf 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/raw_data.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/raw_data.rs.1
@@ -37,16 +37,26 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let resource = extend(storage.read("data", schema::a::resources::DATA));
-        let data = resource.map(|x| flatdata::RawData::new(x))?;
-        let resource = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
-        let optional_data = resource.map(|x| flatdata::RawData::new(x)).ok();
+        let data = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::a::resources::DATA));
+            check("data", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
+        let optional_data = {
+            use flatdata::check_optional_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
+            check("optional_data", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/references.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/references.rs.1
index 37c74825..9bea86e0 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/references.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/references.rs.1
@@ -616,21 +616,29 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let resource = extend(storage.read("list1", schema::a::resources::LIST1));
-        let list1 = resource.map(|x| <&[super::n::S]>::from_bytes(x)).ok().transpose()?;
-        let size = list1.as_ref().map(|r| r.len()).unwrap_or(0);
-        if size > 16 { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "list1", size}); }
-        let resource = extend(storage.read("list2", schema::a::resources::LIST2));
-        let list2 = resource.map(|x| <&[super::n::S]>::from_bytes(x))??;
-        let size = list2.len();
-        if size > 16 { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "list2", size}); }
+        let list1 = {
+            use flatdata::check_optional_resource as check;
+            let max_size = Some(16);
+            let resource = extend(storage.read("list1", schema::a::resources::LIST1));
+            check("list1", |r| r.len(), max_size, resource.and_then(|x| <&[super::n::S]>::from_bytes(x)))?
+        };
+        let list2 = {
+            use flatdata::check_resource as check;
+            let max_size = Some(16);
+            let resource = extend(storage.read("list2", schema::a::resources::LIST2));
+            check("list2", |r| r.len(), max_size, resource.and_then(|x| <&[super::n::S]>::from_bytes(x)))?
+        };
         let multilist1 = {
+            use flatdata::check_optional_resource as check;
+            let max_size = Some(16);
             let index_schema = &format!("index({})", schema::a::resources::MULTILIST1);
             let index = extend(storage.read("multilist1_index", &index_schema));
             let data = extend(storage.read("multilist1", schema::a::resources::MULTILIST1));
@@ -641,14 +649,18 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result.ok()
+            check("multilist1", |r| r.len(), max_size, result)?
         };
-        let size = multilist1.as_ref().map(|r| r.len()).unwrap_or(0);
-        if size > 16 { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "multilist1", size}); }
         let multilist2 = {
+            use flatdata::check_resource as check;
+            let max_size = Some(16);
             let index_schema = &format!("index({})", schema::a::resources::MULTILIST2);
             let index = extend(storage.read("multilist2_index", &index_schema));
             let data = extend(storage.read("multilist2", schema::a::resources::MULTILIST2));
@@ -659,24 +671,36 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result?
+            check("multilist2", |r| r.len(), max_size, result)?
+        };
+        let raw1 = {
+            use flatdata::check_optional_resource as check;
+            let max_size = Some(16);
+            let resource = extend(storage.read("raw1", schema::a::resources::RAW1));
+            check("raw1", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
+        let raw2 = {
+            use flatdata::check_resource as check;
+            let max_size = Some(16);
+            let resource = extend(storage.read("raw2", schema::a::resources::RAW2));
+            check("raw2", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
+        let refs = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("refs", schema::a::resources::REFS));
+            check("refs", |r| r.len(), max_size, resource.and_then(|x| <&[super::n::R]>::from_bytes(x)))?
         };
-        let size = multilist2.len();
-        if size > 16 { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "multilist2", size}); }
-        let resource = extend(storage.read("raw1", schema::a::resources::RAW1));
-        let raw1 = resource.map(|x| flatdata::RawData::new(x)).ok();
-        let size = raw1.as_ref().map(|r| r.len()).unwrap_or(0);
-        if size > 16 { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "raw1", size}); }
-        let resource = extend(storage.read("raw2", schema::a::resources::RAW2));
-        let raw2 = resource.map(|x| flatdata::RawData::new(x))?;
-        let size = raw2.len();
-        if size > 16 { return Err(flatdata::ResourceStorageError::TooBig{resource_name: "raw2", size}); }
-        let resource = extend(storage.read("refs", schema::a::resources::REFS));
-        let refs = resource.map(|x| <&[super::n::R]>::from_bytes(x))??;
         let multirefs = {
+            use flatdata::check_resource as check;
+            let max_size = None;
             let index_schema = &format!("index({})", schema::a::resources::MULTIREFS);
             let index = extend(storage.read("multirefs_index", &index_schema));
             let data = extend(storage.read("multirefs", schema::a::resources::MULTIREFS));
@@ -687,10 +711,14 @@ impl A {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result?
+            check("multirefs", |r| r.len(), max_size, result)?
         };
 
         Ok(Self {
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/struct.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/struct.rs.1
index 036697f8..2a785e63 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/struct.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/struct.rs.1
@@ -153,16 +153,26 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let resource = extend(storage.read("data", schema::a::resources::DATA));
-        let data = resource.map(|x| super::n::S::from_bytes_slice(x))??;
-        let resource = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
-        let optional_data = resource.map(|x| super::n::S::from_bytes_slice(x)).ok().transpose()?;
+        let data = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::a::resources::DATA));
+            check("data", |_| 0, max_size, resource.and_then(|x| super::n::S::from_bytes_slice(x)))?
+        };
+        let optional_data = {
+            use flatdata::check_optional_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
+            check("optional_data", |_| 0, max_size, resource.and_then(|x| super::n::S::from_bytes_slice(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/subarchive.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/subarchive.rs.1
index e04e1994..187aec6f 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/subarchive.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/subarchive.rs.1
@@ -30,14 +30,20 @@ impl X {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("X"), schema::x::X)?;
 
-        let resource = extend(storage.read("payload", schema::x::resources::PAYLOAD));
-        let payload = resource.map(|x| flatdata::RawData::new(x))?;
+        let payload = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("payload", schema::x::resources::PAYLOAD));
+            check("payload", |r| r.len(), max_size, resource.map(|x| flatdata::RawData::new(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
@@ -118,14 +124,24 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let data = super::n::X::open(storage.subdir("data"))?;
-        let optional_data = super::n::X::open(storage.subdir("optional_data")).ok();
+        let data = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            check("data", |_| 0, max_size, super::n::X::open(storage.subdir("data")))?
+        };
+        let optional_data = {
+            use flatdata::check_optional_resource as check;
+            let max_size = None;
+            check("optional_data", |_| 0, max_size, super::n::X::open(storage.subdir("optional_data")))?
+        };
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-generator/tests/generators/rust_expectations/archives/vector.rs.1 b/flatdata-generator/tests/generators/rust_expectations/archives/vector.rs.1
index 1f1f412d..dc0fb38b 100644
--- a/flatdata-generator/tests/generators/rust_expectations/archives/vector.rs.1
+++ b/flatdata-generator/tests/generators/rust_expectations/archives/vector.rs.1
@@ -153,16 +153,26 @@ impl A {
     {
         #[allow(unused_imports)]
         use flatdata::SliceExt;
+        #[allow(unused_variables)]
+        use flatdata::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], flatdata::ResourceStorageError>| -> Result<&'static [u8], flatdata::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("A"), schema::a::A)?;
 
-        let resource = extend(storage.read("data", schema::a::resources::DATA));
-        let data = resource.map(|x| <&[super::n::S]>::from_bytes(x))??;
-        let resource = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
-        let optional_data = resource.map(|x| <&[super::n::S]>::from_bytes(x)).ok().transpose()?;
+        let data = {
+            use flatdata::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::a::resources::DATA));
+            check("data", |r| r.len(), max_size, resource.and_then(|x| <&[super::n::S]>::from_bytes(x)))?
+        };
+        let optional_data = {
+            use flatdata::check_optional_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("optional_data", schema::a::resources::OPTIONAL_DATA));
+            check("optional_data", |r| r.len(), max_size, resource.and_then(|x| <&[super::n::S]>::from_bytes(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-rs/lib/src/test/test_generated.rs b/flatdata-rs/lib/src/test/test_generated.rs
index a1ad8a81..d6fe29e8 100644
--- a/flatdata-rs/lib/src/test/test_generated.rs
+++ b/flatdata-rs/lib/src/test/test_generated.rs
@@ -388,14 +388,20 @@ impl S {
     {
         #[allow(unused_imports)]
         use crate::SliceExt;
+        #[allow(unused_variables)]
+        use crate::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], crate::ResourceStorageError>| -> Result<&'static [u8], crate::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("S"), schema::s::S)?;
 
-        let resource = extend(storage.read("data", schema::s::resources::DATA));
-        let data = resource.map(|x| super::test::A::from_bytes_slice(x))??;
+        let data = {
+            use crate::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::s::resources::DATA));
+            check("data", |_| 0, max_size, resource.and_then(|x| super::test::A::from_bytes_slice(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
@@ -469,14 +475,20 @@ impl X {
     {
         #[allow(unused_imports)]
         use crate::SliceExt;
+        #[allow(unused_variables)]
+        use crate::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], crate::ResourceStorageError>| -> Result<&'static [u8], crate::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("X"), schema::x::X)?;
 
-        let resource = extend(storage.read("data", schema::x::resources::DATA));
-        let data = resource.map(|x| <&[super::test::A]>::from_bytes(x))??;
+        let data = {
+            use crate::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::x::resources::DATA));
+            check("data", |r| r.len(), max_size, resource.and_then(|x| <&[super::test::A]>::from_bytes(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
@@ -562,14 +574,20 @@ impl Y {
     {
         #[allow(unused_imports)]
         use crate::SliceExt;
+        #[allow(unused_variables)]
+        use crate::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], crate::ResourceStorageError>| -> Result<&'static [u8], crate::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("Y"), schema::y::Y)?;
 
-        let resource = extend(storage.read("data", schema::y::resources::DATA));
-        let data = resource.map(|x| <&[super::test::R]>::from_bytes(x))??;
+        let data = {
+            use crate::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("data", schema::y::resources::DATA));
+            check("data", |r| r.len(), max_size, resource.and_then(|x| <&[super::test::R]>::from_bytes(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
@@ -769,13 +787,17 @@ impl Z {
     {
         #[allow(unused_imports)]
         use crate::SliceExt;
+        #[allow(unused_variables)]
+        use crate::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], crate::ResourceStorageError>| -> Result<&'static [u8], crate::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("Z"), schema::z::Z)?;
 
         let ab = {
+            use crate::check_resource as check;
+            let max_size = None;
             let index_schema = &format!("index({})", schema::z::resources::AB);
             let index = extend(storage.read("ab_index", &index_schema));
             let data = extend(storage.read("ab", schema::z::resources::AB));
@@ -786,10 +808,14 @@ impl Z {
                         data
                     ))
                 }
-                (Ok(_), Err(x)) | (Err(x), Ok(_)) => {return Err(x);}
-                (Err(x), Err(_)) => Err(x),
+                // is resource completely missing?
+                (Err(Error::Missing), Err(Error::Missing))  => Err(Error::Missing),
+                // is resource partially missing / broken -> extract best error to propagate
+                (Ok(_), Err(Error::Missing)) | (Err(Error::Missing), Ok(_)) => Err(Error::MissingData),
+                (Err(Error::Missing), Err(x)) | (Err(x), Err(Error::Missing)) => {return Err(x);}
+                (_, Err(x)) | (Err(x), _) => {return Err(x);}
             };
-            result?
+            check("ab", |r| r.len(), max_size, result)?
         };
 
         Ok(Self {
@@ -867,14 +893,20 @@ impl W {
     {
         #[allow(unused_imports)]
         use crate::SliceExt;
+        #[allow(unused_variables)]
+        use crate::ResourceStorageError as Error;
         // extend lifetime since Rust cannot know that we reference a cache here
         #[allow(unused_variables)]
-        let extend = |x : Result<&[u8], crate::ResourceStorageError>| -> Result<&'static [u8], crate::ResourceStorageError> {x.map(|x| unsafe{std::mem::transmute(x)})};
+        let extend = |x : Result<&[u8], Error>| -> Result<&'static [u8], Error> {x.map(|x| unsafe{std::mem::transmute(x)})};
 
         storage.read(&Self::signature_name("W"), schema::w::W)?;
 
-        let resource = extend(storage.read("blob", schema::w::resources::BLOB));
-        let blob = resource.map(|x| crate::RawData::new(x))?;
+        let blob = {
+            use crate::check_resource as check;
+            let max_size = None;
+            let resource = extend(storage.read("blob", schema::w::resources::BLOB));
+            check("blob", |r| r.len(), max_size, resource.map(|x| crate::RawData::new(x)))?
+        };
 
         Ok(Self {
             _storage: storage,
diff --git a/flatdata-rs/tests/features/src/archives/subarchive.rs b/flatdata-rs/tests/features/src/archives/subarchive.rs
index 81f85f93..f420e030 100644
--- a/flatdata-rs/tests/features/src/archives/subarchive.rs
+++ b/flatdata-rs/tests/features/src/archives/subarchive.rs
@@ -2,9 +2,11 @@
 
 include!(concat!(env!("OUT_DIR"), "/archives/subarchive.rs"));
 
+use flatdata::ResourceStorage;
+
 #[test]
 fn test() {
-    for &set_optional in &[false, true] {
+    for &(set_optional, broken) in &[(false, false), (true, false), (true, true)] {
         let storage = flatdata::MemoryResourceStorage::new("/my_test");
 
         let builder = n::ABuilder::new(storage.clone()).expect("Failed to create builder");
@@ -19,8 +21,22 @@ fn test() {
                 .expect("Failed to open optional sub-builder")
                 .set_payload(b"My Other Data")
                 .expect("Failed to set optional data");
+
+            if broken {
+                let mut stream = storage
+                    .create_output_stream("/my_test/optional_data/payload.schema")
+                    .expect("Failed to overwrite schema");
+                stream
+                    .write(b"broken stuff")
+                    .expect("Failed to overwrite schema");
+                stream.flush().expect("Failed to overwrite schema");
+            }
         }
 
+        if broken {
+            n::A::open(storage).expect_err("Should have failed to open archive");
+            continue;
+        }
         let archive = n::A::open(storage).expect("Failed to open archive");
         assert_eq!(archive.data().payload().as_bytes(), b"My Data");
         if set_optional {