From a6929ea4ceae674ede01efd0af0520b0a1fbc790 Mon Sep 17 00:00:00 2001
From: librelois <c@elo.tf>
Date: Fri, 18 Dec 2020 19:01:46 +0100
Subject: [PATCH] [ref] kv_typed: remove StringErr

---
 .cargo/config                                 |   4 +
 Cargo.lock                                    |   1 +
 rust-bins/duniter-dbex/src/export_bc.rs       |   5 +-
 rust-libs/duniter-dbs/Cargo.toml              |   1 +
 rust-libs/duniter-dbs/src/keys/all.rs         |   7 +-
 .../duniter-dbs/src/keys/block_number.rs      |  16 +-
 rust-libs/duniter-dbs/src/keys/blockstamp.rs  |  20 +--
 .../duniter-dbs/src/keys/dunp_node_id.rs      |   6 +-
 rust-libs/duniter-dbs/src/keys/hash.rs        |  18 +-
 rust-libs/duniter-dbs/src/keys/pubkey.rs      |  22 +--
 .../duniter-dbs/src/keys/pubkey_and_sig.rs    |  12 +-
 rust-libs/duniter-dbs/src/keys/source_key.rs  |  16 +-
 rust-libs/duniter-dbs/src/keys/timestamp.rs   |  16 +-
 rust-libs/duniter-dbs/src/keys/ud_id.rs       |  18 +-
 rust-libs/duniter-dbs/src/keys/uid.rs         |  10 +-
 rust-libs/duniter-dbs/src/keys/utxo_id.rs     |  26 +--
 .../duniter-dbs/src/keys/wallet_conditions.rs |  20 +--
 .../src/keys/wallet_hash_with_bn.rs           |  14 +-
 rust-libs/duniter-dbs/src/lib.rs              |   7 +-
 rust-libs/duniter-dbs/src/values/block_db.rs  |  26 +--
 .../duniter-dbs/src/values/block_head_db.rs   |  13 +-
 .../duniter-dbs/src/values/block_meta.rs      |  10 +-
 .../src/values/block_number_array_db.rs       |  13 +-
 rust-libs/duniter-dbs/src/values/cindex_db.rs |  13 +-
 rust-libs/duniter-dbs/src/values/dunp_head.rs |  30 ++--
 .../duniter-dbs/src/values/gva_idty_db.rs     |  12 +-
 rust-libs/duniter-dbs/src/values/idty_db.rs   |  12 +-
 rust-libs/duniter-dbs/src/values/iindex_db.rs |  13 +-
 rust-libs/duniter-dbs/src/values/kick_db.rs   |  13 +-
 rust-libs/duniter-dbs/src/values/mindex_db.rs |  13 +-
 rust-libs/duniter-dbs/src/values/peer_card.rs |  11 +-
 rust-libs/duniter-dbs/src/values/pubkey_db.rs |  30 ++--
 rust-libs/duniter-dbs/src/values/sindex_db.rs |  32 ++--
 .../duniter-dbs/src/values/source_amount.rs   |  18 +-
 rust-libs/duniter-dbs/src/values/tx_db.rs     |  26 +--
 rust-libs/duniter-dbs/src/values/txs.rs       |  13 +-
 .../duniter-dbs/src/values/ud_entry_db.rs     |  13 +-
 rust-libs/duniter-dbs/src/values/utxo.rs      |  18 +-
 rust-libs/duniter-dbs/src/values/wallet_db.rs |  13 +-
 .../src/values/wallet_script_array.rs         |  10 +-
 .../src/values/wallet_script_with_sa.rs       |  10 +-
 rust-libs/duniter-dbs/tests/test_explorer.rs  |  70 ++++----
 .../src/legacy/block_indexer.rs               |   8 +-
 rust-libs/modules/gva/dbs-reader/src/lib.rs   |   9 +-
 .../modules/gva/dbs-reader/src/txs_history.rs |   9 +-
 rust-libs/modules/gva/dbs-reader/src/utxos.rs |  13 +-
 .../tools/kv_typed/src/backend/leveldb.rs     |   8 +-
 rust-libs/tools/kv_typed/src/backend/lmdb.rs  |   4 +-
 .../tools/kv_typed/src/backend/memory.rs      | 156 +-----------------
 .../kv_typed/src/backend/memory_singleton.rs  |   6 +-
 rust-libs/tools/kv_typed/src/backend/sled.rs  |   8 +-
 rust-libs/tools/kv_typed/src/db_schema.rs     |   4 +-
 rust-libs/tools/kv_typed/src/error.rs         |   6 +-
 rust-libs/tools/kv_typed/src/explorer.rs      | 108 +++++++-----
 rust-libs/tools/kv_typed/src/from_bytes.rs    |  29 ++--
 rust-libs/tools/kv_typed/src/iter.rs          |   4 +-
 rust-libs/tools/kv_typed/src/iter/keys.rs     |   2 +-
 rust-libs/tools/kv_typed/src/iter/values.rs   |   2 +-
 rust-libs/tools/kv_typed/src/lib.rs           |   8 +-
 .../tools/kv_typed/src/transactional_write.rs |   2 +-
 rust-libs/tools/kv_typed/src/value.rs         |   6 +-
 61 files changed, 465 insertions(+), 598 deletions(-)

diff --git a/.cargo/config b/.cargo/config
index 1798a5f3a..6268103cb 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -6,3 +6,7 @@ dex = "run --release --package duniter-dbex --"
 ta = "test --all"
 rr = "run --release --"
 xtask = "run --package xtask --"
+
+[target.x86_64-unknown-linux-gnu]
+linker = "/usr/bin/clang"
+rustflags = ["-Clink-arg=-fuse-ld=lld"]
diff --git a/Cargo.lock b/Cargo.lock
index c4894d3c3..206b87e9b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1097,6 +1097,7 @@ dependencies = [
 name = "duniter-dbs"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "arrayvec",
  "bincode",
  "byteorder",
diff --git a/rust-bins/duniter-dbex/src/export_bc.rs b/rust-bins/duniter-dbex/src/export_bc.rs
index 6578aab16..6a38ddff4 100644
--- a/rust-bins/duniter-dbex/src/export_bc.rs
+++ b/rust-bins/duniter-dbex/src/export_bc.rs
@@ -66,8 +66,9 @@ pub(crate) fn export_bc<B: Backend>(
             r.iter()
                 .map(|block_res| {
                     let json_block_res = match block_res {
-                        Ok(block) => serde_json::to_value(&block)
-                            .map_err(|e| KvError::DeserError(e.to_string())),
+                        Ok(block) => {
+                            serde_json::to_value(&block).map_err(|e| KvError::DeserError(e.into()))
+                        }
                         Err(e) => Err(e),
                     };
                     s2.send(json_block_res).map_err(|_| anyhow!("fail to send"))
diff --git a/rust-libs/duniter-dbs/Cargo.toml b/rust-libs/duniter-dbs/Cargo.toml
index 33b35cdb0..9e4484093 100644
--- a/rust-libs/duniter-dbs/Cargo.toml
+++ b/rust-libs/duniter-dbs/Cargo.toml
@@ -30,6 +30,7 @@ uninit = "0.4.0"
 zerocopy = "0.3.0"
 
 [dev-dependencies]
+anyhow = "1.0.34"
 tempdir = "0.3.7"
 unwrap = "1.2.1"
 
diff --git a/rust-libs/duniter-dbs/src/keys/all.rs b/rust-libs/duniter-dbs/src/keys/all.rs
index 067e44616..ff7ad19b7 100644
--- a/rust-libs/duniter-dbs/src/keys/all.rs
+++ b/rust-libs/duniter-dbs/src/keys/all.rs
@@ -25,13 +25,13 @@ impl KeyAsBytes for AllKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for AllKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         if bytes == b"ALL" {
             Ok(Self)
         } else {
-            Err(StringErr(format!(
+            Err(CorruptedBytes(format!(
                 "Invalid key: expected '{:?}', found '{:?}'",
                 b"ALL", bytes
             )))
@@ -47,8 +47,9 @@ impl ToDumpString for AllKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for AllKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         <Self as kv_typed::prelude::FromBytes>::from_bytes(source.as_bytes())
+            .map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
diff --git a/rust-libs/duniter-dbs/src/keys/block_number.rs b/rust-libs/duniter-dbs/src/keys/block_number.rs
index ffb804c67..703f826b2 100644
--- a/rust-libs/duniter-dbs/src/keys/block_number.rs
+++ b/rust-libs/duniter-dbs/src/keys/block_number.rs
@@ -29,18 +29,16 @@ impl KeyAsBytes for BlockNumberKeyV1 {
 }
 
 impl FromBytes for BlockNumberKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let key_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
+        let key_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
         if key_str == "0000000NaN" {
             Ok(BlockNumberKeyV1(BlockNumber(u32::MAX)))
         } else {
-            Ok(BlockNumberKeyV1(BlockNumber(
-                key_str
-                    .parse()
-                    .map_err(|e| StringErr(format!("{}: {}", e, key_str)))?,
-            )))
+            Ok(BlockNumberKeyV1(BlockNumber(key_str.parse().map_err(
+                |e| CorruptedBytes(format!("{}: {}", e, key_str)),
+            )?)))
         }
     }
 }
@@ -53,8 +51,8 @@ impl ToDumpString for BlockNumberKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for BlockNumberKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         Ok(format!("{}", (self.0).0))
diff --git a/rust-libs/duniter-dbs/src/keys/blockstamp.rs b/rust-libs/duniter-dbs/src/keys/blockstamp.rs
index 7ed5c9942..03a0a5c79 100644
--- a/rust-libs/duniter-dbs/src/keys/blockstamp.rs
+++ b/rust-libs/duniter-dbs/src/keys/blockstamp.rs
@@ -25,18 +25,18 @@ impl KeyAsBytes for BlockstampKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for BlockstampKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let blockstamp_strs: ArrayVec<[&str; 2]> = std::str::from_utf8(bytes)
-            .map_err(|e| StringErr(format!("{}", e)))?
+            .map_err(|e| CorruptedBytes(e.to_string()))?
             .split('-')
             .collect();
         let block_number = blockstamp_strs[0]
             .parse()
-            .map_err(|e| StringErr(format!("{}", e)))?;
+            .map_err(|e: ParseIntError| CorruptedBytes(e.to_string()))?;
         let block_hash =
-            Hash::from_hex(blockstamp_strs[1]).map_err(|e| StringErr(format!("{}", e)))?;
+            Hash::from_hex(blockstamp_strs[1]).map_err(|e| CorruptedBytes(e.to_string()))?;
         Ok(BlockstampKeyV1(Blockstamp {
             number: BlockNumber(block_number),
             hash: BlockHash(block_hash),
@@ -52,8 +52,8 @@ impl ToDumpString for BlockstampKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for BlockstampKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         Ok(format!("{}", self.0))
@@ -71,12 +71,12 @@ impl KeyAsBytes for BlockstampKeyV2 {
 }
 
 impl kv_typed::prelude::FromBytes for BlockstampKeyV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         use dubp::common::bytes_traits::FromBytes as _;
         Ok(Self(
-            Blockstamp::from_bytes(bytes).map_err(|e| StringErr(e.to_string()))?,
+            Blockstamp::from_bytes(bytes).map_err(|e| CorruptedBytes(e.to_string()))?,
         ))
     }
 }
@@ -89,9 +89,9 @@ impl ToDumpString for BlockstampKeyV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for BlockstampKeyV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         Ok(Self(
-            Blockstamp::from_str(source).map_err(|e| StringErr(e.to_string()))?,
+            Blockstamp::from_str(source).map_err(|e| FromExplorerKeyErr(e.into()))?,
         ))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
diff --git a/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs b/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs
index e1edbb969..bf65bd9d7 100644
--- a/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs
+++ b/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs
@@ -69,11 +69,11 @@ impl KeyAsBytes for DunpNodeIdV1Db {
 }
 
 impl kv_typed::prelude::FromBytes for DunpNodeIdV1Db {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let layout = zerocopy::LayoutVerified::<_, DunpNodeIdV1Db>::new(bytes)
-            .ok_or_else(|| StringErr("corrupted db".to_owned()))?;
+            .ok_or_else(|| CorruptedBytes("corrupted db".to_owned()))?;
         Ok(*layout)
     }
 }
@@ -86,7 +86,7 @@ impl ToDumpString for DunpNodeIdV1Db {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for DunpNodeIdV1Db {
-    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerKeyErr> {
         unimplemented!()
     }
     fn to_explorer_string(&self) -> KvResult<String> {
diff --git a/rust-libs/duniter-dbs/src/keys/hash.rs b/rust-libs/duniter-dbs/src/keys/hash.rs
index a1d0619d0..1ea68286f 100644
--- a/rust-libs/duniter-dbs/src/keys/hash.rs
+++ b/rust-libs/duniter-dbs/src/keys/hash.rs
@@ -25,12 +25,12 @@ impl KeyAsBytes for HashKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for HashKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let hash_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
+        let hash_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
         Ok(HashKeyV1(
-            Hash::from_hex(&hash_str).map_err(|e| StringErr(format!("{}", e)))?,
+            Hash::from_hex(&hash_str).map_err(|e| CorruptedBytes(e.to_string()))?,
         ))
     }
 }
@@ -43,8 +43,8 @@ impl ToDumpString for HashKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for HashKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
@@ -71,11 +71,11 @@ impl KeyAsBytes for HashKeyV2 {
 }
 
 impl kv_typed::prelude::FromBytes for HashKeyV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         if bytes.len() != 32 {
-            Err(StringErr(format!(
+            Err(CorruptedBytes(format!(
                 "Invalid length: expected 32 found {}",
                 bytes.len()
             )))
@@ -95,9 +95,9 @@ impl ToDumpString for HashKeyV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for HashKeyV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         Ok(Self(
-            Hash::from_hex(source).map_err(|e| StringErr(format!("{}", e)))?,
+            Hash::from_hex(source).map_err(|e| FromExplorerKeyErr(e.into()))?,
         ))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
diff --git a/rust-libs/duniter-dbs/src/keys/pubkey.rs b/rust-libs/duniter-dbs/src/keys/pubkey.rs
index 639b32cbf..9f31d5259 100644
--- a/rust-libs/duniter-dbs/src/keys/pubkey.rs
+++ b/rust-libs/duniter-dbs/src/keys/pubkey.rs
@@ -39,12 +39,12 @@ impl KeyAsBytes for PubKeyKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for PubKeyKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let pubkey_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
+        let pubkey_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
         Ok(PubKeyKeyV1(PublicKey::from_base58(&pubkey_str).map_err(
-            |e| StringErr(format!("{}: {}", e, pubkey_str)),
+            |e| CorruptedBytes(format!("{}: {}", e, pubkey_str)),
         )?))
     }
 }
@@ -65,12 +65,12 @@ impl KeyAsBytes for PubKeyKeyV2 {
 }
 
 impl kv_typed::prelude::FromBytes for PubKeyKeyV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(PubKeyKeyV2(
-            PublicKey::try_from(bytes).map_err(|e| StringErr(format!("{}: {:?}", e, bytes)))?,
-        ))
+        Ok(PubKeyKeyV2(PublicKey::try_from(bytes).map_err(|e| {
+            CorruptedBytes(format!("{}: {:?}", e, bytes))
+        })?))
     }
 }
 
@@ -82,8 +82,8 @@ impl ToDumpString for PubKeyKeyV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for PubKeyKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
@@ -92,9 +92,9 @@ impl ExplorableKey for PubKeyKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for PubKeyKeyV2 {
-    fn from_explorer_str(pubkey_str: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(pubkey_str: &str) -> std::result::Result<Self, FromExplorerKeyErr> {
         Ok(PubKeyKeyV2(PublicKey::from_base58(&pubkey_str).map_err(
-            |e| StringErr(format!("{}: {}", e, pubkey_str)),
+            |e| FromExplorerKeyErr(format!("{}: {}", e, pubkey_str).into()),
         )?))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
diff --git a/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs b/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs
index 65934750d..079d06f07 100644
--- a/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs
+++ b/rust-libs/duniter-dbs/src/keys/pubkey_and_sig.rs
@@ -35,18 +35,18 @@ impl KeyAsBytes for PubKeyAndSigV1 {
 }
 
 impl kv_typed::prelude::FromBytes for PubKeyAndSigV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let raw_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
+        let raw_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
         if raw_str == "ALL" {
             Ok(PubKeyAndSigV1::all())
         } else {
             let array_str: ArrayVec<[&str; 2]> = raw_str.split(':').collect();
             let pubkey =
-                PublicKey::from_base58(array_str[0]).map_err(|e| StringErr(format!("{}", e)))?;
+                PublicKey::from_base58(array_str[0]).map_err(|e| CorruptedBytes(e.to_string()))?;
             let sig =
-                Signature::from_base64(array_str[1]).map_err(|e| StringErr(format!("{}", e)))?;
+                Signature::from_base64(array_str[1]).map_err(|e| CorruptedBytes(e.to_string()))?;
             Ok(PubKeyAndSigV1(pubkey, sig))
         }
     }
@@ -60,8 +60,8 @@ impl ToDumpString for PubKeyAndSigV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for PubKeyAndSigV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
diff --git a/rust-libs/duniter-dbs/src/keys/source_key.rs b/rust-libs/duniter-dbs/src/keys/source_key.rs
index 1c1e781c2..7b63e7a63 100644
--- a/rust-libs/duniter-dbs/src/keys/source_key.rs
+++ b/rust-libs/duniter-dbs/src/keys/source_key.rs
@@ -44,15 +44,17 @@ impl KeyAsBytes for SourceKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for SourceKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let strs: ArrayVec<[&str; 3]> = std::str::from_utf8(bytes)
-            .map_err(|e| StringErr(format!("{}", e)))?
+            .map_err(|e| CorruptedBytes(e.to_string()))?
             .split('-')
             .collect();
-        let tx_hash = Hash::from_hex(strs[0]).map_err(|e| StringErr(format!("{}", e)))?;
-        let pos = strs[1].parse().map_err(|e| StringErr(format!("{}", e)))?;
+        let tx_hash = Hash::from_hex(strs[0]).map_err(|e| CorruptedBytes(e.to_string()))?;
+        let pos = strs[1]
+            .parse()
+            .map_err(|e: ParseIntError| CorruptedBytes(e.to_string()))?;
         let consumed = if strs.len() <= 2 {
             None
         } else {
@@ -60,7 +62,7 @@ impl kv_typed::prelude::FromBytes for SourceKeyV1 {
                 "1" => Some(true),
                 "0" => Some(false),
                 _ => {
-                    return Err(StringErr(
+                    return Err(CorruptedBytes(
                         "invalid format: field consumed must be encoded with '0' or '1'".to_owned(),
                     ))
                 }
@@ -82,8 +84,8 @@ impl ToDumpString for SourceKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for SourceKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
diff --git a/rust-libs/duniter-dbs/src/keys/timestamp.rs b/rust-libs/duniter-dbs/src/keys/timestamp.rs
index 691e04784..f531d2d73 100644
--- a/rust-libs/duniter-dbs/src/keys/timestamp.rs
+++ b/rust-libs/duniter-dbs/src/keys/timestamp.rs
@@ -25,15 +25,13 @@ impl KeyAsBytes for TimestampKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for TimestampKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let key_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
-        Ok(TimestampKeyV1(
-            key_str
-                .parse()
-                .map_err(|e| StringErr(format!("{}: {}", e, key_str)))?,
-        ))
+        let key_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
+        Ok(TimestampKeyV1(key_str.parse().map_err(|e| {
+            CorruptedBytes(format!("{}: {}", e, key_str))
+        })?))
     }
 }
 
@@ -45,10 +43,10 @@ impl ToDumpString for TimestampKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for TimestampKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         NaiveDateTime::parse_from_str(source, "%Y-%m-%d %H:%M:%S")
             .map(|dt| TimestampKeyV1(dt.timestamp() as u64))
-            .map_err(|e| StringErr(format!("{}: {}", e, source)))
+            .map_err(|e| FromExplorerKeyErr(format!("{}: {}", e, source).into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         Ok(NaiveDateTime::from_timestamp(self.0 as i64, 0)
diff --git a/rust-libs/duniter-dbs/src/keys/ud_id.rs b/rust-libs/duniter-dbs/src/keys/ud_id.rs
index 71617b5fd..b7d944855 100644
--- a/rust-libs/duniter-dbs/src/keys/ud_id.rs
+++ b/rust-libs/duniter-dbs/src/keys/ud_id.rs
@@ -47,15 +47,15 @@ impl KeyAsBytes for UdIdV2 {
 }
 
 impl FromBytes for UdIdV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let pubkey = PublicKey::try_from(&bytes[..33])
-            .map_err(|e| StringErr(format!("{}: {:?}", e, bytes)))?;
+            .map_err(|e| CorruptedBytes(format!("{}: {:?}", e, bytes)))?;
         let block_number = BlockNumber(
             zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(&bytes[33..])
                 .ok_or_else(|| {
-                    StringErr(
+                    CorruptedBytes(
                         "Corrupted DB: BlockNumber bytes are invalid length or unaligned"
                             .to_owned(),
                     )
@@ -74,22 +74,22 @@ impl ToDumpString for UdIdV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for UdIdV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         let mut source = source.split(':');
         if let Some(pubkey_str) = source.next() {
             let pubkey = PublicKey::from_base58(&pubkey_str)
-                .map_err(|e| StringErr(format!("{}: {}", e, pubkey_str)))?;
+                .map_err(|e| FromExplorerKeyErr(format!("{}: {}", e, pubkey_str).into()))?;
             if let Some(block_number_str) = source.next() {
                 Ok(UdIdV2(
                     pubkey,
                     BlockNumber::from_str(block_number_str)
-                        .map_err(|e| StringErr(format!("{}", e)))?,
+                        .map_err(|e| FromExplorerKeyErr(e.into()))?,
                 ))
             } else {
-                Err(StringErr("UdIdV2: Invalid format".to_owned()))
+                Err(FromExplorerKeyErr("UdIdV2: Invalid format".into()))
             }
         } else {
-            Err(StringErr("UdIdV2: Invalid format".to_owned()))
+            Err(FromExplorerKeyErr("UdIdV2: Invalid format".into()))
         }
     }
     fn to_explorer_string(&self) -> KvResult<String> {
@@ -102,7 +102,7 @@ mod tests {
     use super::*;
 
     #[test]
-    fn ud_id_v2_as_bytes() -> std::result::Result<(), StringErr> {
+    fn ud_id_v2_as_bytes() -> anyhow::Result<()> {
         let ud_id = UdIdV2(PublicKey::default(), BlockNumber(3));
 
         let ud_id_2_res = ud_id.as_bytes(|bytes| {
diff --git a/rust-libs/duniter-dbs/src/keys/uid.rs b/rust-libs/duniter-dbs/src/keys/uid.rs
index 7a481de4b..86fc05242 100644
--- a/rust-libs/duniter-dbs/src/keys/uid.rs
+++ b/rust-libs/duniter-dbs/src/keys/uid.rs
@@ -27,13 +27,13 @@ impl KeyAsBytes for UidKeyV1 {
 }
 
 impl kv_typed::prelude::FromBytes for UidKeyV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let uid_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
+        let uid_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
         Ok(Self(
             ArrayString::<[u8; USERNAME_MAX_LEN]>::from_str(uid_str)
-                .map_err(|e| StringErr(format!("{}", e)))?,
+                .map_err(|e| CorruptedBytes(e.to_string()))?,
         ))
     }
 }
@@ -56,8 +56,8 @@ impl ToDumpString for UidKeyV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for UidKeyV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
diff --git a/rust-libs/duniter-dbs/src/keys/utxo_id.rs b/rust-libs/duniter-dbs/src/keys/utxo_id.rs
index fbb83f083..d0936c976 100644
--- a/rust-libs/duniter-dbs/src/keys/utxo_id.rs
+++ b/rust-libs/duniter-dbs/src/keys/utxo_id.rs
@@ -49,16 +49,16 @@ impl KeyAsBytes for UtxoIdDbV2 {
 }
 
 impl FromBytes for UtxoIdDbV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let hash = zerocopy::LayoutVerified::<_, Hash>::new(&bytes[..32]).ok_or_else(|| {
-            StringErr("Corrupted DB: Hash bytes are invalid length or unaligned".to_owned())
+            CorruptedBytes("Corrupted DB: Hash bytes are invalid length or unaligned".to_owned())
         })?;
         let output_index =
             zerocopy::LayoutVerified::<_, zerocopy::U32<byteorder::BigEndian>>::new(&bytes[32..])
                 .ok_or_else(|| {
-                    StringErr(
+                    CorruptedBytes(
                         "Corrupted DB: OutputIndex bytes are invalid length or unaligned"
                             .to_owned(),
                     )
@@ -76,21 +76,21 @@ impl ToDumpString for UtxoIdDbV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for UtxoIdDbV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         let mut source = source.split(':');
         if let Some(hash_str) = source.next() {
-            let hash =
-                Hash::from_hex(&hash_str).map_err(|e| StringErr(format!("{}: {}", e, hash_str)))?;
+            let hash = Hash::from_hex(&hash_str)
+                .map_err(|e| FromExplorerKeyErr(format!("{}: {}", e, hash_str).into()))?;
             if let Some(output_index_str) = source.next() {
                 Ok(UtxoIdDbV2(
                     hash,
-                    u32::from_str(output_index_str).map_err(|e| StringErr(format!("{}", e)))?,
+                    u32::from_str(output_index_str).map_err(|e| FromExplorerKeyErr(e.into()))?,
                 ))
             } else {
-                Err(StringErr("UtxoIdDbV2: Invalid format".to_owned()))
+                Err(FromExplorerKeyErr("UtxoIdDbV2: Invalid format".into()))
             }
         } else {
-            Err(StringErr("UtxoIdDbV2: Invalid format".to_owned()))
+            Err(FromExplorerKeyErr("UtxoIdDbV2: Invalid format".into()))
         }
     }
     fn to_explorer_string(&self) -> KvResult<String> {
@@ -202,7 +202,7 @@ impl KeyAsBytes for GvaUtxoIdDbV1 {
 }
 
 impl FromBytes for GvaUtxoIdDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         if bytes.len() == 69 {
@@ -214,14 +214,14 @@ impl FromBytes for GvaUtxoIdDbV1 {
             buffer.copy_from_slice(bytes);
             Ok(Self(buffer))
         } else {
-            Err(StringErr("db corrupted".to_owned()))
+            Err(CorruptedBytes("db corrupted".to_owned()))
         }
     }
 }
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for GvaUtxoIdDbV1 {
-    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerKeyErr> {
         unimplemented!()
     }
     fn to_explorer_string(&self) -> KvResult<String> {
@@ -234,7 +234,7 @@ mod tests {
     use super::*;
 
     #[test]
-    fn utxo_id_v2_as_bytes() -> std::result::Result<(), StringErr> {
+    fn utxo_id_v2_as_bytes() -> anyhow::Result<()> {
         let utxo_id = UtxoIdDbV2(Hash::default(), 3);
 
         let utxo_id_2_res = utxo_id.as_bytes(|bytes| {
diff --git a/rust-libs/duniter-dbs/src/keys/wallet_conditions.rs b/rust-libs/duniter-dbs/src/keys/wallet_conditions.rs
index ea909dbc3..0a389b386 100644
--- a/rust-libs/duniter-dbs/src/keys/wallet_conditions.rs
+++ b/rust-libs/duniter-dbs/src/keys/wallet_conditions.rs
@@ -27,13 +27,13 @@ impl KeyAsBytes for WalletConditionsV1 {
 }
 
 impl kv_typed::prelude::FromBytes for WalletConditionsV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        let uid_str = std::str::from_utf8(bytes).map_err(|e| StringErr(format!("{}", e)))?;
+        let uid_str = std::str::from_utf8(bytes).map_err(|e| CorruptedBytes(e.to_string()))?;
         Ok(Self(
             ArrayString::<[u8; CONDITIONS_MAX_LEN]>::from_str(uid_str)
-                .map_err(|e| StringErr(format!("{}", e)))?,
+                .map_err(|e| CorruptedBytes(e.to_string()))?,
         ))
     }
 }
@@ -56,8 +56,8 @@ impl ToDumpString for WalletConditionsV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for WalletConditionsV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerKeyErr(e.0.into()))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
         self.as_bytes(|bytes| Ok(unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()))
@@ -85,12 +85,10 @@ impl KeyAsBytes for WalletConditionsV2 {
 }
 
 impl kv_typed::prelude::FromBytes for WalletConditionsV2 {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(Self(
-            bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?,
-        ))
+        Ok(Self(bincode::deserialize(bytes)?))
     }
 }
 
@@ -102,10 +100,10 @@ impl ToDumpString for WalletConditionsV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for WalletConditionsV2 {
-    fn from_explorer_str(s: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(s: &str) -> std::result::Result<Self, FromExplorerKeyErr> {
         Ok(Self(
             dubp::documents_parser::wallet_script_from_str(s)
-                .map_err(|e| StringErr(format!("{}", e)))?,
+                .map_err(|e| FromExplorerKeyErr(e.into()))?,
         ))
     }
     fn to_explorer_string(&self) -> KvResult<String> {
diff --git a/rust-libs/duniter-dbs/src/keys/wallet_hash_with_bn.rs b/rust-libs/duniter-dbs/src/keys/wallet_hash_with_bn.rs
index 25b3d32d9..4ee7dc3c7 100644
--- a/rust-libs/duniter-dbs/src/keys/wallet_hash_with_bn.rs
+++ b/rust-libs/duniter-dbs/src/keys/wallet_hash_with_bn.rs
@@ -70,11 +70,11 @@ impl KeyAsBytes for WalletHashWithBnV1Db {
 }
 
 impl kv_typed::prelude::FromBytes for WalletHashWithBnV1Db {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let layout = zerocopy::LayoutVerified::<_, WalletHashWithBnV1Db>::new(bytes)
-            .ok_or_else(|| StringErr("corrupted db".to_owned()))?;
+            .ok_or_else(|| CorruptedBytes("corrupted db".to_owned()))?;
         Ok(*layout)
     }
 }
@@ -91,19 +91,19 @@ impl ToDumpString for WalletHashWithBnV1Db {
 
 #[cfg(feature = "explorer")]
 impl ExplorableKey for WalletHashWithBnV1Db {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         let mut source = source.split(':');
         let hash_str = source
             .next()
-            .ok_or_else(|| StringErr("missing hash".to_owned()))?;
+            .ok_or_else(|| FromExplorerKeyErr("missing hash".into()))?;
         let bn_str = source
             .next()
-            .ok_or_else(|| StringErr("missing block number".to_owned()))?;
+            .ok_or_else(|| FromExplorerKeyErr("missing block number".into()))?;
 
-        let hash = Hash::from_hex(hash_str).map_err(|e| StringErr(e.to_string()))?;
+        let hash = Hash::from_hex(hash_str).map_err(|e| FromExplorerKeyErr(e.into()))?;
         let block_number = bn_str
             .parse()
-            .map_err(|e: std::num::ParseIntError| StringErr(e.to_string()))?;
+            .map_err(|e: std::num::ParseIntError| FromExplorerKeyErr(e.into()))?;
 
         Ok(WalletHashWithBnV1Db::new(hash, block_number))
     }
diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs
index 57cc8f2bd..7004bd5b0 100644
--- a/rust-libs/duniter-dbs/src/lib.rs
+++ b/rust-libs/duniter-dbs/src/lib.rs
@@ -106,9 +106,14 @@ pub(crate) use kv_typed::prelude::*;
 pub(crate) use serde::{Deserialize, Serialize};
 pub(crate) use smallvec::SmallVec;
 pub(crate) use std::{
-    collections::BTreeSet, convert::TryFrom, fmt::Debug, iter::Iterator, path::Path, str::FromStr,
+    collections::BTreeSet, convert::TryFrom, fmt::Debug, iter::Iterator, num::ParseIntError,
+    path::Path, str::FromStr,
 };
 
+#[derive(Debug, Error)]
+#[error("{0}")]
+pub struct CorruptedBytes(pub String);
+
 pub trait ToDumpString {
     fn to_dump_string(&self) -> String;
 }
diff --git a/rust-libs/duniter-dbs/src/values/block_db.rs b/rust-libs/duniter-dbs/src/values/block_db.rs
index 2e83199e3..f13c51b8b 100644
--- a/rust-libs/duniter-dbs/src/values/block_db.rs
+++ b/rust-libs/duniter-dbs/src/values/block_db.rs
@@ -66,19 +66,18 @@ pub struct BlockDbV1 {
 
 impl ValueAsBytes for BlockDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for BlockDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -90,11 +89,11 @@ impl ToDumpString for BlockDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
@@ -123,16 +122,17 @@ pub struct BlockDbV2(pub dubp::block::DubpBlockV10);
 
 impl ValueAsBytes for BlockDbV2 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(bytes.as_ref())
     }
 }
 
 impl kv_typed::prelude::FromBytes for BlockDbV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?)
+        Ok(bincode::deserialize(&bytes)
+            .map_err(|e| CorruptedBytes(format!("{}: '{:?}'", e, bytes)))?)
     }
 }
 
@@ -144,10 +144,10 @@ impl ToDumpString for BlockDbV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockDbV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Ok(serde_json::from_str(source).map_err(|e| StringErr(e.to_string()))?)
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Ok(serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))?)
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/block_head_db.rs b/rust-libs/duniter-dbs/src/values/block_head_db.rs
index 99d101406..cc125ad65 100644
--- a/rust-libs/duniter-dbs/src/values/block_head_db.rs
+++ b/rust-libs/duniter-dbs/src/values/block_head_db.rs
@@ -53,19 +53,18 @@ pub struct BlockHeadDbV1 {
 
 impl ValueAsBytes for BlockHeadDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for BlockHeadDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -77,10 +76,10 @@ impl ToDumpString for BlockHeadDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockHeadDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/block_meta.rs b/rust-libs/duniter-dbs/src/values/block_meta.rs
index 0bba4c5c2..3cc59b677 100644
--- a/rust-libs/duniter-dbs/src/values/block_meta.rs
+++ b/rust-libs/duniter-dbs/src/values/block_meta.rs
@@ -59,10 +59,10 @@ impl ValueAsBytes for BlockMetaV2 {
 }
 
 impl kv_typed::prelude::FromBytes for BlockMetaV2 {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+        Ok(bincode::deserialize(bytes)?)
     }
 }
 
@@ -74,12 +74,12 @@ impl ToDumpString for BlockMetaV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockMetaV2 {
-    fn from_explorer_str(json_str: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(json_str: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| FromExplorerValueErr(format!("{}: '{}'", e, json_str).into()))?)
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
diff --git a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs
index 9fbf01ae2..b365c9eb3 100644
--- a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs
+++ b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs
@@ -20,20 +20,19 @@ pub struct BlockNumberArrayV1(pub SmallVec<[BlockNumber; 1]>);
 
 impl ValueAsBytes for BlockNumberArrayV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json_string =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json_string = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(format!("[{}]", json_string).as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for BlockNumberArrayV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         //println!("json_str='{}'", &json_str);
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -45,10 +44,10 @@ impl ToDumpString for BlockNumberArrayV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockNumberArrayV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/cindex_db.rs b/rust-libs/duniter-dbs/src/values/cindex_db.rs
index d3d160a94..bbb744dab 100644
--- a/rust-libs/duniter-dbs/src/values/cindex_db.rs
+++ b/rust-libs/duniter-dbs/src/values/cindex_db.rs
@@ -23,20 +23,19 @@ pub struct CIndexDbV1 {
 
 impl ValueAsBytes for CIndexDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json_string =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json_string = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(format!("[{}]", json_string).as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for CIndexDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         //println!("json_str='{}'", &json_str);
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -48,11 +47,11 @@ impl ToDumpString for CIndexDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for CIndexDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
diff --git a/rust-libs/duniter-dbs/src/values/dunp_head.rs b/rust-libs/duniter-dbs/src/values/dunp_head.rs
index 2ab352e9b..5952fb3a4 100644
--- a/rust-libs/duniter-dbs/src/values/dunp_head.rs
+++ b/rust-libs/duniter-dbs/src/values/dunp_head.rs
@@ -31,21 +31,19 @@ pub struct DunpHeadDbV1 {
 impl DunpHeadDbV1 {
     pub fn from_stringified(message_v2: &str, signature: &str) -> KvResult<(DunpNodeIdV1Db, Self)> {
         let signature =
-            Signature::from_base64(signature).map_err(|e| KvError::DeserError(e.to_string()))?;
+            Signature::from_base64(signature).map_err(|e| KvError::DeserError(e.into()))?;
 
         let strs: SmallVec<[&str; 11]> = message_v2.split(':').collect();
         if strs.len() < 11 {
             return Err(KvError::DeserError(
-                "DunpHeadDbV1::from_stringified(): invalid message_v2".to_owned(),
+                "DunpHeadDbV1::from_stringified(): invalid message_v2".into(),
             ));
         }
 
-        let uuid =
-            u32::from_str_radix(strs[5], 16).map_err(|e| KvError::DeserError(e.to_string()))?;
-        let pubkey =
-            PublicKey::from_base58(strs[3]).map_err(|e| KvError::DeserError(e.to_string()))?;
+        let uuid = u32::from_str_radix(strs[5], 16).map_err(|e| KvError::DeserError(e.into()))?;
+        let pubkey = PublicKey::from_base58(strs[3]).map_err(|e| KvError::DeserError(e.into()))?;
         let blockstamp =
-            Blockstamp::from_str(strs[4]).map_err(|e| KvError::DeserError(e.to_string()))?;
+            Blockstamp::from_str(strs[4]).map_err(|e| KvError::DeserError(e.into()))?;
 
         Ok((
             DunpNodeIdV1Db::new(uuid, pubkey),
@@ -55,12 +53,11 @@ impl DunpHeadDbV1 {
                 blockstamp,
                 software: strs[6].to_owned(),
                 software_version: strs[7].to_owned(),
-                pow_prefix: u32::from_str(strs[8])
-                    .map_err(|e| KvError::DeserError(e.to_string()))?,
+                pow_prefix: u32::from_str(strs[8]).map_err(|e| KvError::DeserError(e.into()))?,
                 free_member_room: u32::from_str(strs[9])
-                    .map_err(|e| KvError::DeserError(e.to_string()))?,
+                    .map_err(|e| KvError::DeserError(e.into()))?,
                 free_mirror_room: u32::from_str(strs[10])
-                    .map_err(|e| KvError::DeserError(e.to_string()))?,
+                    .map_err(|e| KvError::DeserError(e.into()))?,
                 signature,
             },
         ))
@@ -97,16 +94,17 @@ mod tests {
 
 impl ValueAsBytes for DunpHeadDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(bytes.as_ref())
     }
 }
 
 impl kv_typed::prelude::FromBytes for DunpHeadDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?)
+        Ok(bincode::deserialize(&bytes)
+            .map_err(|e| CorruptedBytes(format!("{}: '{:?}'", e, bytes)))?)
     }
 }
 
@@ -118,10 +116,10 @@ impl ToDumpString for DunpHeadDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for DunpHeadDbV1 {
-    fn from_explorer_str(_source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_source: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         unimplemented!()
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/gva_idty_db.rs b/rust-libs/duniter-dbs/src/values/gva_idty_db.rs
index 946ee97ec..abebd0a76 100644
--- a/rust-libs/duniter-dbs/src/values/gva_idty_db.rs
+++ b/rust-libs/duniter-dbs/src/values/gva_idty_db.rs
@@ -25,15 +25,15 @@ pub struct GvaIdtyDbV1 {
 
 impl ValueAsBytes for GvaIdtyDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
 
 impl kv_typed::prelude::FromBytes for GvaIdtyDbV1 {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+        Ok(bincode::deserialize(bytes)?)
     }
 }
 
@@ -45,10 +45,10 @@ impl ToDumpString for GvaIdtyDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for GvaIdtyDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        serde_json::from_str(source).map_err(|e| StringErr(format!("{}", e)))
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(&self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(&self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/idty_db.rs b/rust-libs/duniter-dbs/src/values/idty_db.rs
index fb4b352e7..3b9f34f8c 100644
--- a/rust-libs/duniter-dbs/src/values/idty_db.rs
+++ b/rust-libs/duniter-dbs/src/values/idty_db.rs
@@ -23,15 +23,15 @@ pub struct IdtyDbV2 {
 
 impl ValueAsBytes for IdtyDbV2 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
 
 impl kv_typed::prelude::FromBytes for IdtyDbV2 {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+        Ok(bincode::deserialize(bytes)?)
     }
 }
 
@@ -43,10 +43,10 @@ impl ToDumpString for IdtyDbV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for IdtyDbV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        serde_json::from_str(source).map_err(|e| StringErr(format!("{}", e)))
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(&self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(&self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/iindex_db.rs b/rust-libs/duniter-dbs/src/values/iindex_db.rs
index a56e81052..a2c71d081 100644
--- a/rust-libs/duniter-dbs/src/values/iindex_db.rs
+++ b/rust-libs/duniter-dbs/src/values/iindex_db.rs
@@ -20,20 +20,19 @@ pub struct IIndexDbV1(pub SmallVec<[IIndexLineDbV1; 1]>);
 
 impl ValueAsBytes for IIndexDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json_string =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json_string = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(format!("[{}]", json_string).as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for IIndexDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         //println!("json_str='{}'", &json_str);
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -45,11 +44,11 @@ impl ToDumpString for IIndexDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for IIndexDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
diff --git a/rust-libs/duniter-dbs/src/values/kick_db.rs b/rust-libs/duniter-dbs/src/values/kick_db.rs
index 8d7c93f50..dddf5f703 100644
--- a/rust-libs/duniter-dbs/src/values/kick_db.rs
+++ b/rust-libs/duniter-dbs/src/values/kick_db.rs
@@ -23,19 +23,18 @@ pub struct KickDbV1 {
 
 impl ValueAsBytes for KickDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for KickDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -47,10 +46,10 @@ impl ToDumpString for KickDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for KickDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/mindex_db.rs b/rust-libs/duniter-dbs/src/values/mindex_db.rs
index bee30646d..f08285940 100644
--- a/rust-libs/duniter-dbs/src/values/mindex_db.rs
+++ b/rust-libs/duniter-dbs/src/values/mindex_db.rs
@@ -20,20 +20,19 @@ pub struct MIndexDbV1(pub SmallVec<[MIndexLineDbV1; 1]>);
 
 impl ValueAsBytes for MIndexDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json_string =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json_string = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(format!("[{}]", json_string).as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for MIndexDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         //println!("json_str='{}'", &json_str);
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -45,11 +44,11 @@ impl ToDumpString for MIndexDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for MIndexDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
diff --git a/rust-libs/duniter-dbs/src/values/peer_card.rs b/rust-libs/duniter-dbs/src/values/peer_card.rs
index c51c1c44d..36751b1a6 100644
--- a/rust-libs/duniter-dbs/src/values/peer_card.rs
+++ b/rust-libs/duniter-dbs/src/values/peer_card.rs
@@ -28,16 +28,17 @@ pub struct PeerCardDbV1 {
 
 impl ValueAsBytes for PeerCardDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(bytes.as_ref())
     }
 }
 
 impl kv_typed::prelude::FromBytes for PeerCardDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?)
+        Ok(bincode::deserialize(&bytes)
+            .map_err(|e| CorruptedBytes(format!("{}: '{:?}'", e, bytes)))?)
     }
 }
 
@@ -49,10 +50,10 @@ impl ToDumpString for PeerCardDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for PeerCardDbV1 {
-    fn from_explorer_str(_source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_source: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         unimplemented!()
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/pubkey_db.rs b/rust-libs/duniter-dbs/src/values/pubkey_db.rs
index 17be3a752..64022e99e 100644
--- a/rust-libs/duniter-dbs/src/values/pubkey_db.rs
+++ b/rust-libs/duniter-dbs/src/values/pubkey_db.rs
@@ -45,9 +45,9 @@ impl ToDumpString for PublicKeySingletonDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for PublicKeySingletonDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
         Ok(Self(
-            PublicKey::from_base58(source).map_err(|e| StringErr(format!("{}", e)))?,
+            PublicKey::from_base58(source).map_err(|e| FromExplorerValueErr(e.into()))?,
         ))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
@@ -65,24 +65,24 @@ impl ValueAsBytes for PublicKeyArrayDbV1 {
             .iter()
             .map(|pubkey| pubkey.to_base58())
             .collect::<SmallVec<[String; 8]>>();
-        let json = serde_json::to_string(&vec_pub_str)
-            .map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json =
+            serde_json::to_string(&vec_pub_str).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for PublicKeyArrayDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         let vec_pub_str: SmallVec<[String; 8]> = serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?;
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?;
         Ok(Self(
             vec_pub_str
                 .into_iter()
                 .map(|pub_str| {
-                    PublicKey::from_base58(&pub_str).map_err(|e| StringErr(format!("{}", e)))
+                    PublicKey::from_base58(&pub_str).map_err(|e| CorruptedBytes(e.to_string()))
                 })
                 .collect::<std::result::Result<SmallVec<[PublicKey; 8]>, Self::Err>>()?,
         ))
@@ -97,8 +97,8 @@ impl ToDumpString for PublicKeyArrayDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for PublicKeyArrayDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
         Ok(serde_json::Value::Array(
@@ -122,12 +122,12 @@ impl ValueAsBytes for PubKeyValV2 {
 }
 
 impl kv_typed::prelude::FromBytes for PubKeyValV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(PubKeyValV2(
-            PublicKey::try_from(bytes).map_err(|e| StringErr(format!("{}: {:?}", e, bytes)))?,
-        ))
+        Ok(PubKeyValV2(PublicKey::try_from(bytes).map_err(|e| {
+            CorruptedBytes(format!("{}: {:?}", e, bytes))
+        })?))
     }
 }
 
@@ -139,9 +139,9 @@ impl ToDumpString for PubKeyValV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for PubKeyValV2 {
-    fn from_explorer_str(pubkey_str: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(pubkey_str: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         Ok(PubKeyValV2(PublicKey::from_base58(&pubkey_str).map_err(
-            |e| StringErr(format!("{}: {}", e, pubkey_str)),
+            |e| FromExplorerValueErr(format!("{}: {}", e, pubkey_str).into()),
         )?))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
diff --git a/rust-libs/duniter-dbs/src/values/sindex_db.rs b/rust-libs/duniter-dbs/src/values/sindex_db.rs
index 27f7ed0db..3a7ab670b 100644
--- a/rust-libs/duniter-dbs/src/values/sindex_db.rs
+++ b/rust-libs/duniter-dbs/src/values/sindex_db.rs
@@ -44,19 +44,18 @@ pub struct SIndexDBV1 {
 
 impl ValueAsBytes for SIndexDBV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for SIndexDBV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -68,11 +67,11 @@ impl ToDumpString for SIndexDBV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for SIndexDBV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
@@ -86,26 +85,23 @@ impl ValueAsBytes for SourceKeyArrayDbV1 {
             .iter()
             .map(|source_key| source_key.to_string())
             .collect::<SmallVec<[String; 8]>>();
-        let json = serde_json::to_string(&vec_pub_str)
-            .map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json =
+            serde_json::to_string(&vec_pub_str).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for SourceKeyArrayDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         let vec_source_key_str: SmallVec<[String; 8]> = serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?;
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?;
         Ok(Self(
             vec_source_key_str
                 .into_iter()
-                .map(|source_key_str| {
-                    SourceKeyV1::from_bytes(source_key_str.as_bytes())
-                        .map_err(|e| StringErr(format!("{}", e)))
-                })
+                .map(|source_key_str| SourceKeyV1::from_bytes(source_key_str.as_bytes()))
                 .collect::<std::result::Result<SmallVec<[SourceKeyV1; 8]>, Self::Err>>()?,
         ))
     }
@@ -119,10 +115,10 @@ impl ToDumpString for SourceKeyArrayDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for SourceKeyArrayDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/source_amount.rs b/rust-libs/duniter-dbs/src/values/source_amount.rs
index bcb954a83..bde2dfd58 100644
--- a/rust-libs/duniter-dbs/src/values/source_amount.rs
+++ b/rust-libs/duniter-dbs/src/values/source_amount.rs
@@ -26,11 +26,11 @@ impl ValueAsBytes for SourceAmountValV2 {
 }
 
 impl kv_typed::prelude::FromBytes for SourceAmountValV2 {
-    type Err = StringErr;
+    type Err = LayoutVerifiedErr;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let layout = zerocopy::LayoutVerified::<_, SourceAmount>::new(bytes)
-            .ok_or_else(|| StringErr("".to_owned()))?;
+            .ok_or(LayoutVerifiedErr(stringify!(SourceAmount)))?;
         Ok(Self(*layout))
     }
 }
@@ -43,18 +43,18 @@ impl ToDumpString for SourceAmountValV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for SourceAmountValV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
         let mut source = source.split(':');
         let amount_str = source
             .next()
-            .ok_or_else(|| StringErr("Missing amount".to_owned()))?;
+            .ok_or_else(|| FromExplorerValueErr("Missing amount".into()))?;
         let base_str = source
             .next()
-            .ok_or_else(|| StringErr("Missing base".to_owned()))?;
-        let amount =
-            i64::from_str(amount_str).map_err(|e| StringErr(format!("Invalid amount: {}", e)))?;
-        let base =
-            i64::from_str(base_str).map_err(|e| StringErr(format!("Invalid base: {}", e)))?;
+            .ok_or_else(|| FromExplorerValueErr("Missing base".into()))?;
+        let amount = i64::from_str(amount_str)
+            .map_err(|e| FromExplorerValueErr(format!("Invalid amount: {}", e).into()))?;
+        let base = i64::from_str(base_str)
+            .map_err(|e| FromExplorerValueErr(format!("Invalid base: {}", e).into()))?;
         Ok(Self(SourceAmount::new(amount, base)))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
diff --git a/rust-libs/duniter-dbs/src/values/tx_db.rs b/rust-libs/duniter-dbs/src/values/tx_db.rs
index 602270816..2afe3979d 100644
--- a/rust-libs/duniter-dbs/src/values/tx_db.rs
+++ b/rust-libs/duniter-dbs/src/values/tx_db.rs
@@ -21,16 +21,17 @@ pub struct PendingTxDbV2(pub TransactionDocumentV10);
 
 impl ValueAsBytes for PendingTxDbV2 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(bytes.as_ref())
     }
 }
 
 impl kv_typed::prelude::FromBytes for PendingTxDbV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?)
+        Ok(bincode::deserialize(&bytes)
+            .map_err(|e| CorruptedBytes(format!("{}: '{:?}'", e, bytes)))?)
     }
 }
 
@@ -42,11 +43,11 @@ impl ToDumpString for PendingTxDbV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for PendingTxDbV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
@@ -59,16 +60,17 @@ pub struct TxDbV2 {
 
 impl ValueAsBytes for TxDbV2 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(bytes.as_ref())
     }
 }
 
 impl kv_typed::prelude::FromBytes for TxDbV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?)
+        Ok(bincode::deserialize(&bytes)
+            .map_err(|e| CorruptedBytes(format!("{}: '{:?}'", e, bytes)))?)
     }
 }
 
@@ -80,10 +82,10 @@ impl ToDumpString for TxDbV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for TxDbV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/txs.rs b/rust-libs/duniter-dbs/src/values/txs.rs
index c7b01b41b..25867244c 100644
--- a/rust-libs/duniter-dbs/src/values/txs.rs
+++ b/rust-libs/duniter-dbs/src/values/txs.rs
@@ -21,16 +21,17 @@ pub struct BlockTxsDbV2(pub SmallVec<[TransactionDocumentV10; 8]>);
 
 impl ValueAsBytes for BlockTxsDbV2 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(bytes.as_ref())
     }
 }
 
 impl kv_typed::prelude::FromBytes for BlockTxsDbV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?)
+        Ok(bincode::deserialize(&bytes)
+            .map_err(|e| CorruptedBytes(format!("{}: '{:?}'", e, bytes)))?)
     }
 }
 
@@ -42,10 +43,10 @@ impl ToDumpString for BlockTxsDbV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockTxsDbV2 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Ok(serde_json::from_str(source).map_err(|e| StringErr(e.to_string()))?)
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Ok(serde_json::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))?)
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/ud_entry_db.rs b/rust-libs/duniter-dbs/src/values/ud_entry_db.rs
index 13bf96baa..a9e8c2dde 100644
--- a/rust-libs/duniter-dbs/src/values/ud_entry_db.rs
+++ b/rust-libs/duniter-dbs/src/values/ud_entry_db.rs
@@ -30,19 +30,18 @@ pub struct UdEntryDbV1 {
 
 impl ValueAsBytes for UdEntryDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for UdEntryDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -54,11 +53,11 @@ impl ToDumpString for UdEntryDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for UdEntryDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
 
diff --git a/rust-libs/duniter-dbs/src/values/utxo.rs b/rust-libs/duniter-dbs/src/values/utxo.rs
index bfbfd358b..96b4ae3e1 100644
--- a/rust-libs/duniter-dbs/src/values/utxo.rs
+++ b/rust-libs/duniter-dbs/src/values/utxo.rs
@@ -69,7 +69,7 @@ impl std::fmt::Display for UtxoValV2 {
 }
 
 impl FromStr for UtxoValV2 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_str(_s: &str) -> std::result::Result<Self, Self::Err> {
         unimplemented!()
@@ -83,11 +83,11 @@ impl ValueAsBytes for UtxoValV2 {
 }
 
 impl kv_typed::prelude::FromBytes for UtxoValV2 {
-    type Err = StringErr;
+    type Err = LayoutVerifiedErr;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let layout = zerocopy::LayoutVerified::<_, UtxoValV2>::new(bytes)
-            .ok_or_else(|| StringErr("corrupted db".to_owned()))?;
+            .ok_or(LayoutVerifiedErr(stringify!(UtxoValV2)))?;
         Ok(*layout)
     }
 }
@@ -100,7 +100,7 @@ impl ToDumpString for UtxoValV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for UtxoValV2 {
-    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         unimplemented!()
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
@@ -113,15 +113,15 @@ pub struct BlockUtxosV2Db(pub HashMap<UtxoIdV10, WalletScriptWithSourceAmountV1D
 
 impl ValueAsBytes for BlockUtxosV2Db {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
 
 impl kv_typed::prelude::FromBytes for BlockUtxosV2Db {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+        Ok(bincode::deserialize(bytes)?)
     }
 }
 
@@ -133,11 +133,11 @@ impl ToDumpString for BlockUtxosV2Db {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for BlockUtxosV2Db {
-    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         unimplemented!()
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        Ok(serde_json::to_value(self).map_err(|e| KvError::DeserError(e.to_string()))?)
+        Ok(serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
 
diff --git a/rust-libs/duniter-dbs/src/values/wallet_db.rs b/rust-libs/duniter-dbs/src/values/wallet_db.rs
index 17dac89c5..56747c219 100644
--- a/rust-libs/duniter-dbs/src/values/wallet_db.rs
+++ b/rust-libs/duniter-dbs/src/values/wallet_db.rs
@@ -23,19 +23,18 @@ pub struct WalletDbV1 {
 
 impl ValueAsBytes for WalletDbV1 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        let json =
-            serde_json::to_string(self).map_err(|e| KvError::DeserError(format!("{}", e)))?;
+        let json = serde_json::to_string(self).map_err(|e| KvError::DeserError(e.into()))?;
         f(json.as_bytes())
     }
 }
 
 impl kv_typed::prelude::FromBytes for WalletDbV1 {
-    type Err = StringErr;
+    type Err = CorruptedBytes;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
         let json_str = std::str::from_utf8(bytes).expect("corrupted db : invalid utf8 bytes");
         Ok(serde_json::from_str(&json_str)
-            .map_err(|e| StringErr(format!("{}: '{}'", e, json_str)))?)
+            .map_err(|e| CorruptedBytes(format!("{}: '{}'", e, json_str)))?)
     }
 }
 
@@ -47,10 +46,10 @@ impl ToDumpString for WalletDbV1 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for WalletDbV1 {
-    fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-        Self::from_bytes(source.as_bytes())
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+        Self::from_bytes(source.as_bytes()).map_err(|e| FromExplorerValueErr(e.0.into()))
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
+        serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))
     }
 }
diff --git a/rust-libs/duniter-dbs/src/values/wallet_script_array.rs b/rust-libs/duniter-dbs/src/values/wallet_script_array.rs
index c99debd74..7f3e3dd71 100644
--- a/rust-libs/duniter-dbs/src/values/wallet_script_array.rs
+++ b/rust-libs/duniter-dbs/src/values/wallet_script_array.rs
@@ -20,17 +20,15 @@ pub struct WalletScriptArrayV2(pub std::collections::HashSet<WalletScriptV10>);
 
 impl ValueAsBytes for WalletScriptArrayV2 {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        f(&bincode::serialize(&self.0).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+        f(&bincode::serialize(&self.0).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
 
 impl kv_typed::prelude::FromBytes for WalletScriptArrayV2 {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(Self(
-            bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?,
-        ))
+        Ok(Self(bincode::deserialize(bytes)?))
     }
 }
 
@@ -42,7 +40,7 @@ impl ToDumpString for WalletScriptArrayV2 {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for WalletScriptArrayV2 {
-    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         unimplemented!()
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
diff --git a/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs b/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs
index def12e505..94e917a46 100644
--- a/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs
+++ b/rust-libs/duniter-dbs/src/values/wallet_script_with_sa.rs
@@ -23,15 +23,15 @@ pub struct WalletScriptWithSourceAmountV1Db {
 
 impl ValueAsBytes for WalletScriptWithSourceAmountV1Db {
     fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> {
-        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(format!("{}", e)))?)
+        f(&bincode::serialize(&self).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
 
 impl kv_typed::prelude::FromBytes for WalletScriptWithSourceAmountV1Db {
-    type Err = StringErr;
+    type Err = bincode::Error;
 
     fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-        Ok(bincode::deserialize(bytes).map_err(|e| StringErr(format!("{}", e)))?)
+        bincode::deserialize(bytes)
     }
 }
 
@@ -43,10 +43,10 @@ impl ToDumpString for WalletScriptWithSourceAmountV1Db {
 
 #[cfg(feature = "explorer")]
 impl ExplorableValue for WalletScriptWithSourceAmountV1Db {
-    fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> std::result::Result<Self, FromExplorerValueErr> {
         unimplemented!()
     }
     fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        Ok(serde_json::to_value(self).map_err(|e| KvError::DeserError(e.to_string()))?)
+        Ok(serde_json::to_value(self).map_err(|e| KvError::DeserError(e.into()))?)
     }
 }
diff --git a/rust-libs/duniter-dbs/tests/test_explorer.rs b/rust-libs/duniter-dbs/tests/test_explorer.rs
index 76e0b7a9c..51cfc5acc 100644
--- a/rust-libs/duniter-dbs/tests/test_explorer.rs
+++ b/rust-libs/duniter-dbs/tests/test_explorer.rs
@@ -37,7 +37,7 @@ mod explorer {
     }
 
     #[test]
-    fn explorer_test_leveldb() -> KvResult<()> {
+    fn explorer_test_leveldb() -> anyhow::Result<()> {
         let tmp_dir = unwrap!(TempDir::new("explorer_test_leveldb"));
 
         let db = BcV1Db::<LevelDb>::open(LevelDbConf::path(tmp_dir.path().to_owned()))?;
@@ -46,13 +46,13 @@ mod explorer {
     }
 
     #[test]
-    fn explorer_test_sled() -> KvResult<()> {
+    fn explorer_test_sled() -> anyhow::Result<()> {
         let db = BcV1Db::<Sled>::open(SledConf::new().temporary(true))?;
 
         explorer_test(&db)
     }
 
-    fn explorer_test<B: Backend>(db: &BcV1Db<B>) -> KvResult<()> {
+    fn explorer_test<B: Backend>(db: &BcV1Db<B>) -> anyhow::Result<()> {
         // Defines test data
         let k1 = unwrap!(UidKeyV1::from_str("toto"));
         let k2 = unwrap!(UidKeyV1::from_str("titi"));
@@ -72,26 +72,24 @@ mod explorer {
             COLLECTION_NAME,
             ExplorerAction::Count,
             stringify_json_value_test,
-        )?;
-        assert_eq!(Ok(ExplorerActionResponse::Count(2)), res);
+        )??;
+        assert_eq!(ExplorerActionResponse::Count(2), res);
 
         // Test action get
         let res = db.explore(
             COLLECTION_NAME,
             ExplorerAction::Get { key: "unexist" },
             stringify_json_value_test,
-        )?;
-        assert_eq!(Ok(ExplorerActionResponse::Get(None)), res);
+        )??;
+        assert_eq!(ExplorerActionResponse::Get(None), res);
         let res = db.explore(
             COLLECTION_NAME,
             ExplorerAction::Get { key: "toto" },
             stringify_json_value_test,
-        )?;
+        )??;
         assert_eq!(
-            Ok(ExplorerActionResponse::Get(Some(
-                serde_json::Value::String(
-                    "ByE9TU6qhktHYYVAqeTcWcaULBx151siQLyL3TrKvY85".to_owned()
-                )
+            ExplorerActionResponse::Get(Some(serde_json::Value::String(
+                "ByE9TU6qhktHYYVAqeTcWcaULBx151siQLyL3TrKvY85".to_owned()
             ))),
             res
         );
@@ -104,18 +102,16 @@ mod explorer {
                 value: "Bi6ECSc352gdfEvVzGiQuuDQyaTptHkcxooMGTJk14Tr",
             },
             stringify_json_value_test,
-        )?;
-        assert_eq!(Ok(ExplorerActionResponse::PutOk), res);
+        )??;
+        assert_eq!(ExplorerActionResponse::PutOk, res);
         let res = db.explore(
             COLLECTION_NAME,
             ExplorerAction::Get { key: "titu" },
             stringify_json_value_test,
-        )?;
+        )??;
         assert_eq!(
-            Ok(ExplorerActionResponse::Get(Some(
-                serde_json::Value::String(
-                    "Bi6ECSc352gdfEvVzGiQuuDQyaTptHkcxooMGTJk14Tr".to_owned()
-                )
+            ExplorerActionResponse::Get(Some(serde_json::Value::String(
+                "Bi6ECSc352gdfEvVzGiQuuDQyaTptHkcxooMGTJk14Tr".to_owned()
             ))),
             res
         );
@@ -123,8 +119,8 @@ mod explorer {
             COLLECTION_NAME,
             ExplorerAction::Count,
             stringify_json_value_test,
-        )?;
-        assert_eq!(Ok(ExplorerActionResponse::Count(3)), res);
+        )??;
+        assert_eq!(ExplorerActionResponse::Count(3), res);
 
         // Test action find
         let range_res = db.explore(
@@ -139,9 +135,9 @@ mod explorer {
                 step: unsafe { NonZeroUsize::new_unchecked(1) },
             },
             stringify_json_value_test,
-        )?;
+        )??;
         assert_eq!(
-            Ok(ExplorerActionResponse::Find(vec![
+            ExplorerActionResponse::Find(vec![
                 EntryFound {
                     key: "titi".to_owned(),
                     value: serde_json::Value::String(
@@ -156,7 +152,7 @@ mod explorer {
                     ),
                     captures: None,
                 },
-            ])),
+            ]),
             range_res
         );
 
@@ -173,15 +169,15 @@ mod explorer {
                 step: unsafe { NonZeroUsize::new_unchecked(1) },
             },
             stringify_json_value_test,
-        )?;
+        )??;
         assert_eq!(
-            Ok(ExplorerActionResponse::Find(vec![EntryFound {
+            ExplorerActionResponse::Find(vec![EntryFound {
                 key: "titi".to_owned(),
                 value: serde_json::Value::String(
                     "8B5XCAHknsckCkMWeGF9FoGibSNZXF9HtAvzxzg3bSyp".to_owned()
                 ),
                 captures: None,
-            }])),
+            }]),
             range_res
         );
 
@@ -198,15 +194,15 @@ mod explorer {
                 step: unsafe { NonZeroUsize::new_unchecked(1) },
             },
             stringify_json_value_test,
-        )?;
+        )??;
         assert_eq!(
-            Ok(ExplorerActionResponse::Find(vec![EntryFound {
+            ExplorerActionResponse::Find(vec![EntryFound {
                 key: "titu".to_owned(),
                 value: serde_json::Value::String(
                     "Bi6ECSc352gdfEvVzGiQuuDQyaTptHkcxooMGTJk14Tr".to_owned()
                 ),
                 captures: None,
-            }])),
+            }]),
             range_res
         );
 
@@ -223,9 +219,9 @@ mod explorer {
                 step: unsafe { NonZeroUsize::new_unchecked(1) },
             },
             stringify_json_value_test,
-        )?;
+        )??;
         assert_eq!(
-            Ok(ExplorerActionResponse::Find(vec![EntryFound {
+            ExplorerActionResponse::Find(vec![EntryFound {
                 key: "titu".to_owned(),
                 value: serde_json::Value::String(
                     "Bi6ECSc352gdfEvVzGiQuuDQyaTptHkcxooMGTJk14Tr".to_owned()
@@ -234,7 +230,7 @@ mod explorer {
                     smallvec![Some("EC".to_owned())],
                     smallvec![Some("Ev".to_owned())]
                 ])),
-            }])),
+            }]),
             range_res
         );
 
@@ -243,14 +239,14 @@ mod explorer {
             COLLECTION_NAME,
             ExplorerAction::Delete { key: "toto" },
             stringify_json_value_test,
-        )?;
-        assert_eq!(Ok(ExplorerActionResponse::DeleteOk), res);
+        )??;
+        assert_eq!(ExplorerActionResponse::DeleteOk, res);
         let res = db.explore(
             COLLECTION_NAME,
             ExplorerAction::Get { key: "toto" },
             stringify_json_value_test,
-        )?;
-        assert_eq!(Ok(ExplorerActionResponse::Get(None)), res);
+        )??;
+        assert_eq!(ExplorerActionResponse::Get(None), res);
 
         Ok(())
     }
diff --git a/rust-libs/duniter-server/src/legacy/block_indexer.rs b/rust-libs/duniter-server/src/legacy/block_indexer.rs
index 4b3483e1c..906f1300e 100644
--- a/rust-libs/duniter-server/src/legacy/block_indexer.rs
+++ b/rust-libs/duniter-server/src/legacy/block_indexer.rs
@@ -18,8 +18,7 @@ use crate::*;
 impl DuniterServer {
     pub fn apply_block(&mut self, block: DubpBlockV10Stringified) -> KvResult<()> {
         let block = Arc::new(
-            DubpBlockV10::from_string_object(&block)
-                .map_err(|e| KvError::DeserError(format!("{}", e)))?,
+            DubpBlockV10::from_string_object(&block).map_err(|e| KvError::DeserError(e.into()))?,
         );
         self.current = Some(duniter_dbs_write_ops::apply_block::apply_block(
             &self.bc_db,
@@ -37,7 +36,7 @@ impl DuniterServer {
                 .into_iter()
                 .map(|block| DubpBlockV10::from_string_object(&block))
                 .collect::<Result<Vec<_>, _>>()
-                .map_err(|e| KvError::DeserError(format!("{}", e)))?,
+                .map_err(|e| KvError::DeserError(e.into()))?,
         );
         self.current = Some(duniter_dbs_write_ops::apply_block::apply_chunk(
             &self.bc_db,
@@ -49,8 +48,7 @@ impl DuniterServer {
     }
     pub fn revert_block(&mut self, block: DubpBlockV10Stringified) -> KvResult<()> {
         let block = Arc::new(
-            DubpBlockV10::from_string_object(&block)
-                .map_err(|e| KvError::DeserError(format!("{}", e)))?,
+            DubpBlockV10::from_string_object(&block).map_err(|e| KvError::DeserError(e.into()))?,
         );
         let block_arc_clone = Arc::clone(&block);
         let txs_mp_job_handle = self
diff --git a/rust-libs/modules/gva/dbs-reader/src/lib.rs b/rust-libs/modules/gva/dbs-reader/src/lib.rs
index 10d743aff..5d346812c 100644
--- a/rust-libs/modules/gva/dbs-reader/src/lib.rs
+++ b/rust-libs/modules/gva/dbs-reader/src/lib.rs
@@ -58,9 +58,14 @@ use std::{
     str::FromStr,
 };
 
-pub(crate) fn wrong_cursor() -> StringErr {
-    StringErr("wrong cursor".to_owned())
+#[derive(Clone, Copy, Debug)]
+pub struct WrongCursor;
+impl std::fmt::Display for WrongCursor {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "wrong cursor")
+    }
 }
+impl std::error::Error for WrongCursor {}
 
 #[derive(Clone, Copy, Debug)]
 pub struct DbsReader(&'static GvaV1DbRo<FileBackend>);
diff --git a/rust-libs/modules/gva/dbs-reader/src/txs_history.rs b/rust-libs/modules/gva/dbs-reader/src/txs_history.rs
index e4b44bc86..3f08ce954 100644
--- a/rust-libs/modules/gva/dbs-reader/src/txs_history.rs
+++ b/rust-libs/modules/gva/dbs-reader/src/txs_history.rs
@@ -28,17 +28,16 @@ impl std::fmt::Display for TxBcCursor {
 }
 
 impl FromStr for TxBcCursor {
-    type Err = StringErr;
+    type Err = WrongCursor;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let mut s = s.split(':');
         let block_number = s
             .next()
-            .ok_or_else(wrong_cursor)?
+            .ok_or(WrongCursor)?
             .parse()
-            .map_err(|_| wrong_cursor())?;
-        let tx_hash =
-            Hash::from_hex(s.next().ok_or_else(wrong_cursor)?).map_err(|_| wrong_cursor())?;
+            .map_err(|_| WrongCursor)?;
+        let tx_hash = Hash::from_hex(s.next().ok_or(WrongCursor)?).map_err(|_| WrongCursor)?;
         Ok(Self {
             block_number,
             tx_hash,
diff --git a/rust-libs/modules/gva/dbs-reader/src/utxos.rs b/rust-libs/modules/gva/dbs-reader/src/utxos.rs
index 32b5a0bec..d7fa50535 100644
--- a/rust-libs/modules/gva/dbs-reader/src/utxos.rs
+++ b/rust-libs/modules/gva/dbs-reader/src/utxos.rs
@@ -35,22 +35,21 @@ impl std::fmt::Display for UtxoCursor {
 }
 
 impl FromStr for UtxoCursor {
-    type Err = StringErr;
+    type Err = WrongCursor;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let mut s = s.split(':');
         let block_number = s
             .next()
-            .ok_or_else(wrong_cursor)?
+            .ok_or(WrongCursor)?
             .parse()
-            .map_err(|_| wrong_cursor())?;
-        let tx_hash =
-            Hash::from_hex(s.next().ok_or_else(wrong_cursor)?).map_err(|_| wrong_cursor())?;
+            .map_err(|_| WrongCursor)?;
+        let tx_hash = Hash::from_hex(s.next().ok_or(WrongCursor)?).map_err(|_| WrongCursor)?;
         let output_index = s
             .next()
-            .ok_or_else(wrong_cursor)?
+            .ok_or(WrongCursor)?
             .parse()
-            .map_err(|_| wrong_cursor())?;
+            .map_err(|_| WrongCursor)?;
         Ok(Self {
             block_number,
             tx_hash,
diff --git a/rust-libs/tools/kv_typed/src/backend/leveldb.rs b/rust-libs/tools/kv_typed/src/backend/leveldb.rs
index f71034741..1ecd676c8 100644
--- a/rust-libs/tools/kv_typed/src/backend/leveldb.rs
+++ b/rust-libs/tools/kv_typed/src/backend/leveldb.rs
@@ -134,9 +134,7 @@ impl BackendCol for LevelDbCol {
         k.as_bytes(|k_bytes| {
             self.0
                 .get(ReadOptions::new(), k_bytes)?
-                .map(|bytes| {
-                    V::from_bytes(&bytes).map_err(|e| KvError::DeserError(format!("{}", e)))
-                })
+                .map(|bytes| V::from_bytes(&bytes).map_err(|e| KvError::DeserError(e.into())))
                 .transpose()
         })
     }
@@ -156,7 +154,7 @@ impl BackendCol for LevelDbCol {
                         f(&layout_verified)
                     } else {
                         Err(KvError::DeserError(
-                            "Bytes are invalid length or alignment.".to_owned(),
+                            "Bytes are invalid length or alignment.".into(),
                         ))
                     }
                 })
@@ -181,7 +179,7 @@ impl BackendCol for LevelDbCol {
                         f(&layout_verified)
                     } else {
                         Err(KvError::DeserError(
-                            "Bytes are invalid length or alignment.".to_owned(),
+                            "Bytes are invalid length or alignment.".into(),
                         ))
                     }
                 })
diff --git a/rust-libs/tools/kv_typed/src/backend/lmdb.rs b/rust-libs/tools/kv_typed/src/backend/lmdb.rs
index 1190fd550..a74d911c6 100644
--- a/rust-libs/tools/kv_typed/src/backend/lmdb.rs
+++ b/rust-libs/tools/kv_typed/src/backend/lmdb.rs
@@ -270,9 +270,7 @@ impl BackendCol for LmdbCol {
             access
                 .get(&self.inner.tree, k_bytes)
                 .to_opt()?
-                .map(|bytes| {
-                    V::from_bytes(&bytes).map_err(|e| KvError::DeserError(format!("{}", e)))
-                })
+                .map(|bytes| V::from_bytes(&bytes).map_err(|e| KvError::DeserError(e.into())))
                 .transpose()
         })
     }
diff --git a/rust-libs/tools/kv_typed/src/backend/memory.rs b/rust-libs/tools/kv_typed/src/backend/memory.rs
index fd3709c5c..f9c41870f 100644
--- a/rust-libs/tools/kv_typed/src/backend/memory.rs
+++ b/rust-libs/tools/kv_typed/src/backend/memory.rs
@@ -73,154 +73,6 @@ pub struct MemCol {
     tree: Tree,
 }
 
-/*impl MemCol {
-    fn from_file(file_path: std::path::PathBuf) -> KvResult<Self> {
-        let mut file = std::fs::File::open(file_path.as_path())?;
-        let bytes = Vec::<u8>::new();
-        if file.metadata()?.len() > 0 {
-            let mut bytes = Vec::new();
-            use std::io::Read as _;
-            file.read_to_end(&mut bytes)?;
-        }
-
-        Ok(MemCol {
-            path: Some(file_path),
-            tree: Self::tree_from_bytes(&bytes)?,
-        })
-    }
-    fn tree_from_bytes(bytes: &[u8]) -> KvResult<BTreeMap<IVec, IVec>> {
-        let mut tree = BTreeMap::new();
-
-        if bytes.len() < 32 {
-            return Err(KvError::BackendError(
-                StringErr("Corrupted tree".to_owned()).into(),
-            ));
-        } else {
-            let hash = blake3::hash(&bytes[32..]);
-            if hash.as_bytes() != &bytes[..32] {
-                return Err(KvError::BackendError(
-                    StringErr("Corrupted tree: wrong hash".to_owned()).into(),
-                ));
-            }
-        };
-
-        let mut len = 32;
-        while len < bytes.len() {
-            let len_add_4 = len + 4;
-            if bytes.len() >= len_add_4 {
-                let mut k_len = [0u8; 4];
-                k_len.copy_from_slice(&bytes[len..len_add_4]);
-                let k_len = u32::from_le_bytes(k_len);
-                len = len_add_4 + k_len as usize;
-
-                if bytes.len() >= len {
-                    let k = IVec::from(&bytes[len_add_4..len]);
-
-                    if bytes.len() >= len_add_4 {
-                        let len_add_4 = len + 4;
-                        let mut v_len = [0u8; 4];
-                        v_len.copy_from_slice(&bytes[len..len_add_4]);
-                        let v_len = u32::from_le_bytes(v_len);
-                        len = len_add_4 + v_len as usize;
-
-                        if bytes.len() >= len {
-                            let v = IVec::from(&bytes[len_add_4..len]);
-
-                            tree.insert(k, v);
-                        } else {
-                            return Err(KvError::BackendError(
-                                StringErr("Corrupted tree".to_owned()).into(),
-                            ));
-                        }
-                    } else {
-                        return Err(KvError::BackendError(
-                            StringErr("Corrupted tree".to_owned()).into(),
-                        ));
-                    }
-                } else {
-                    return Err(KvError::BackendError(
-                        StringErr("Corrupted tree".to_owned()).into(),
-                    ));
-                }
-            } else {
-                return Err(KvError::BackendError(
-                    StringErr("Corrupted tree".to_owned()).into(),
-                ));
-            }
-        }
-
-        Ok(tree)
-    }
-    fn tree_to_bytes(tree: &BTreeMap<IVec, IVec>) -> Vec<u8> {
-        let mut bytes = Vec::with_capacity(tree.len() * 20);
-        let mut len = 32;
-        for (k, v) in tree.iter() {
-            // Write key len
-            let k_len = (k.len() as u32).to_le_bytes();
-            let len_add_4 = len + 4;
-            bytes.reserve_uninit(4).r().copy_from_slice(&k_len[..]);
-            unsafe {
-                // # Safety
-                //
-                //   - `.copy_from_slice()` contract guarantees initialization
-                //     of 4 additional bytes, which, in turn, from `reserve_uninit`'s contract,
-                //     leads to the `vec` extra capacity having been initialized.
-                bytes.set_len(len_add_4)
-            }
-
-            // Write key content
-            bytes
-                .reserve_uninit(k.len())
-                .r()
-                .copy_from_slice(k.as_ref());
-            let new_len = len_add_4 + k.len();
-            unsafe {
-                // # Safety
-                //
-                //   - `.copy_from_slice()` contract guarantees initialization
-                //     of `k.len()` additional bytes, which, in turn, from `reserve_uninit`'s contract,
-                //     leads to the `vec` extra capacity having been initialized.
-                bytes.set_len(new_len)
-            }
-            len = new_len;
-
-            // Write value len
-            let v_len = (v.len() as u32).to_le_bytes();
-            let len_add_4 = len + 4;
-            bytes.reserve_uninit(4).r().copy_from_slice(&v_len[..]);
-            unsafe {
-                // # Safety
-                //
-                //   - `.copy_from_slice()` contract guarantees initialization
-                //     of 4 additional bytes, which, in turn, from `reserve_uninit`'s contract,
-                //     leads to the `vec` extra capacity having been initialized.
-                bytes.set_len(len_add_4)
-            }
-
-            // Write value content
-            bytes
-                .reserve_uninit(v.len())
-                .r()
-                .copy_from_slice(v.as_ref());
-            let new_len = len_add_4 + v.len();
-            unsafe {
-                // # Safety
-                //
-                //   - `.copy_from_slice()` contract guarantees initialization
-                //     of `v.len()` additional bytes, which, in turn, from `reserve_uninit`'s contract,
-                //     leads to the `vec` extra capacity having been initialized.
-                bytes.set_len(new_len)
-            }
-            len = new_len;
-        }
-
-        let hash = blake3::hash(&bytes[32..]);
-        (&mut bytes[..32]).copy_from_slice(hash.as_bytes());
-
-        bytes
-    }
-}*/
-
 impl BackendCol for MemCol {
     type Batch = MemBatch;
     type KeyBytes = KeyBytes;
@@ -249,9 +101,7 @@ impl BackendCol for MemCol {
         k.as_bytes(|k_bytes| {
             self.tree
                 .get(k_bytes)
-                .map(|bytes| {
-                    V::from_bytes(&bytes).map_err(|e| KvError::DeserError(format!("{}", e)))
-                })
+                .map(|bytes| V::from_bytes(&bytes).map_err(|e| KvError::DeserError(e.into())))
                 .transpose()
         })
     }
@@ -271,7 +121,7 @@ impl BackendCol for MemCol {
                         f(&layout_verified)
                     } else {
                         Err(KvError::DeserError(
-                            "Bytes are invalid length or alignment.".to_owned(),
+                            "Bytes are invalid length or alignment.".into(),
                         ))
                     }
                 })
@@ -296,7 +146,7 @@ impl BackendCol for MemCol {
                         f(&layout_verified)
                     } else {
                         Err(KvError::DeserError(
-                            "Bytes are invalid length or alignment.".to_owned(),
+                            "Bytes are invalid length or alignment.".into(),
                         ))
                     }
                 })
diff --git a/rust-libs/tools/kv_typed/src/backend/memory_singleton.rs b/rust-libs/tools/kv_typed/src/backend/memory_singleton.rs
index d77bcedda..5f49a8cf3 100644
--- a/rust-libs/tools/kv_typed/src/backend/memory_singleton.rs
+++ b/rust-libs/tools/kv_typed/src/backend/memory_singleton.rs
@@ -88,7 +88,7 @@ impl BackendCol for MemCol {
     fn get<K: Key, V: Value>(&self, _k: &K) -> KvResult<Option<V>> {
         self.0
             .as_ref()
-            .map(|bytes| V::from_bytes(bytes).map_err(|e| KvError::DeserError(format!("{}", e))))
+            .map(|bytes| V::from_bytes(bytes).map_err(|e| KvError::DeserError(e.into())))
             .transpose()
     }
     #[inline(always)]
@@ -106,7 +106,7 @@ impl BackendCol for MemCol {
                     f(&layout_verified)
                 } else {
                     Err(KvError::DeserError(
-                        "Bytes are invalid length or alignment.".to_owned(),
+                        "Bytes are invalid length or alignment.".into(),
                     ))
                 }
             })
@@ -127,7 +127,7 @@ impl BackendCol for MemCol {
                     f(&layout_verified)
                 } else {
                     Err(KvError::DeserError(
-                        "Bytes are invalid length or alignment.".to_owned(),
+                        "Bytes are invalid length or alignment.".into(),
                     ))
                 }
             })
diff --git a/rust-libs/tools/kv_typed/src/backend/sled.rs b/rust-libs/tools/kv_typed/src/backend/sled.rs
index 6efd6cb21..3f97ef6de 100644
--- a/rust-libs/tools/kv_typed/src/backend/sled.rs
+++ b/rust-libs/tools/kv_typed/src/backend/sled.rs
@@ -84,9 +84,7 @@ impl BackendCol for SledCol {
         k.as_bytes(|k_bytes| {
             self.0
                 .get(k_bytes)?
-                .map(|bytes| {
-                    V::from_bytes(&bytes).map_err(|e| KvError::DeserError(format!("{}", e)))
-                })
+                .map(|bytes| V::from_bytes(&bytes).map_err(|e| KvError::DeserError(e.into())))
                 .transpose()
         })
     }
@@ -106,7 +104,7 @@ impl BackendCol for SledCol {
                         f(&layout_verified)
                     } else {
                         Err(KvError::DeserError(
-                            "Bytes are invalid length or alignment.".to_owned(),
+                            "Bytes are invalid length or alignment.".into(),
                         ))
                     }
                 })
@@ -131,7 +129,7 @@ impl BackendCol for SledCol {
                         f(&layout_verified)
                     } else {
                         Err(KvError::DeserError(
-                            "Bytes are invalid length or alignment.".to_owned(),
+                            "Bytes are invalid length or alignment.".into(),
                         ))
                     }
                 })
diff --git a/rust-libs/tools/kv_typed/src/db_schema.rs b/rust-libs/tools/kv_typed/src/db_schema.rs
index 3c4835031..1f846e429 100644
--- a/rust-libs/tools/kv_typed/src/db_schema.rs
+++ b/rust-libs/tools/kv_typed/src/db_schema.rs
@@ -77,11 +77,11 @@ macro_rules! db_schema {
                         collection_name: &str,
                         action: kv_typed::explorer::ExplorerAction<'a>,
                         stringify_json_value: fn(serde_json::Value) -> serde_json::Value,
-                    ) -> KvResult<std::result::Result<kv_typed::explorer::ExplorerActionResponse, StringErr>> {
+                    ) -> KvResult<std::result::Result<kv_typed::explorer::ExplorerActionResponse, ExplorerActionErr>> {
                         $( if stringify!([<$col_name:snake>]) == collection_name {
                             return action.exec(&self.collections.[<$col_name:snake>], stringify_json_value);
                         } )*
-                        Ok(Err(StringErr(format!("collection '{}' not exist in database '{}'.", collection_name, stringify!([<$db_name Db>])))))
+                        Ok(Err(ExplorerActionErr(format!("collection '{}' not exist in database '{}'.", collection_name, stringify!([<$db_name Db>])).into())))
                     }
                     fn list_collections() -> Vec<(&'static str, &'static str, &'static str)> {
                         vec![
diff --git a/rust-libs/tools/kv_typed/src/error.rs b/rust-libs/tools/kv_typed/src/error.rs
index 361b04cac..8762ef816 100644
--- a/rust-libs/tools/kv_typed/src/error.rs
+++ b/rust-libs/tools/kv_typed/src/error.rs
@@ -17,10 +17,6 @@
 
 use crate::*;
 
-#[derive(Clone, Debug, Error, PartialEq)]
-#[error("{0}")]
-pub struct StringErr(pub String);
-
 pub type DynErr = Box<dyn Error + Send + Sync + 'static>;
 
 /// KV Typed error
@@ -44,7 +40,7 @@ pub enum KvError {
     DbCorrupted(String),
     // Error at serialisation or deserialisation
     #[error("DeserError: {0}")]
-    DeserError(String),
+    DeserError(DynErr),
     /// FailToCreateDbFolder
     #[error("FailToCreateDbFolder: {0}")]
     FailToCreateDbFolder(std::io::Error),
diff --git a/rust-libs/tools/kv_typed/src/explorer.rs b/rust-libs/tools/kv_typed/src/explorer.rs
index 1d5926db8..e24c75484 100644
--- a/rust-libs/tools/kv_typed/src/explorer.rs
+++ b/rust-libs/tools/kv_typed/src/explorer.rs
@@ -8,17 +8,25 @@ pub trait DbExplorable {
         collection_name: &str,
         action: ExplorerAction<'a>,
         stringify_json_value: fn(serde_json::Value) -> serde_json::Value,
-    ) -> KvResult<Result<ExplorerActionResponse, StringErr>>;
+    ) -> KvResult<Result<ExplorerActionResponse, ExplorerActionErr>>;
     fn list_collections() -> Vec<(&'static str, &'static str, &'static str)>;
 }
 
+#[derive(Debug, Error)]
+#[error("Fail to parse key: {0}")]
+pub struct FromExplorerKeyErr(pub DynErr);
+
+#[derive(Debug, Error)]
+#[error("Fail to parse value: {0}")]
+pub struct FromExplorerValueErr(pub DynErr);
+
 pub trait ExplorableKey: Sized {
-    fn from_explorer_str(source: &str) -> Result<Self, StringErr>;
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr>;
     fn to_explorer_string(&self) -> KvResult<String>;
 }
 
 impl ExplorableKey for () {
-    fn from_explorer_str(_: &str) -> Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> Result<Self, FromExplorerKeyErr> {
         Ok(())
     }
 
@@ -28,7 +36,7 @@ impl ExplorableKey for () {
 }
 
 impl ExplorableKey for String {
-    fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
         Ok(source.to_owned())
     }
 
@@ -40,8 +48,8 @@ impl ExplorableKey for String {
 macro_rules! impl_explorable_key_for_numbers {
     ($($T:ty),*) => {$(
         impl ExplorableKey for $T {
-            fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
-                source.parse().map_err(|e| StringErr(format!("{}", e)))
+            fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+                source.parse().map_err(|e| FromExplorerKeyErr(Box::new(e)))
             }
 
             fn to_explorer_string(&self) -> KvResult<String> {
@@ -55,8 +63,8 @@ impl_explorable_key_for_numbers!(usize, u8, u16, u32, u64, u128, i8, i16, i32, i
 macro_rules! impl_explorable_key_for_be_numbers {
     ($($T:ty),*) => {$(
         impl ExplorableKey for $T {
-            fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
-                Ok(Self(source.parse().map_err(|e| StringErr(format!("{}", e)))?))
+            fn from_explorer_str(source: &str) -> Result<Self, FromExplorerKeyErr> {
+                Ok(Self(source.parse().map_err(|e| FromExplorerKeyErr(Box::new(e)))?))
             }
 
             fn to_explorer_string(&self) -> KvResult<String> {
@@ -68,12 +76,12 @@ macro_rules! impl_explorable_key_for_be_numbers {
 impl_explorable_key_for_be_numbers!(U32BE);
 
 pub trait ExplorableValue: Sized {
-    fn from_explorer_str(source: &str) -> Result<Self, StringErr>;
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr>;
     fn to_explorer_json(&self) -> KvResult<serde_json::Value>;
 }
 
 impl ExplorableValue for () {
-    fn from_explorer_str(_: &str) -> Result<Self, StringErr> {
+    fn from_explorer_str(_: &str) -> Result<Self, FromExplorerValueErr> {
         Ok(())
     }
 
@@ -83,7 +91,7 @@ impl ExplorableValue for () {
 }
 
 impl ExplorableValue for String {
-    fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
         Ok(source.to_owned())
     }
 
@@ -95,8 +103,8 @@ impl ExplorableValue for String {
 macro_rules! impl_explorable_value_for_numbers {
     ($($T:ty),*) => {$(
         impl ExplorableValue for $T {
-            fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
-                source.parse().map_err(|e| StringErr(format!("{}", e)))
+            fn from_explorer_str(source: &str) -> Result<Self, FromExplorerValueErr> {
+                source.parse().map_err(|e| FromExplorerValueErr(Box::new(e)))
             }
 
             #[allow(trivial_numeric_casts)]
@@ -114,23 +122,27 @@ impl_explorable_value_for_numbers!(
 impl<T, E> ExplorableValue for Vec<T>
 where
     T: Display + FromStr<Err = E>,
-    E: Display,
+    E: Error + Send + Sync + 'static,
 {
-    fn from_explorer_str(source: &str) -> Result<Vec<T>, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<Vec<T>, FromExplorerValueErr> {
         if let serde_json::Value::Array(json_array) =
-            serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
+            serde_json::Value::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))?
         {
             let mut vec = Vec::with_capacity(json_array.len());
             for value in json_array {
                 if let serde_json::Value::String(string) = value {
-                    vec.push(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?);
+                    vec.push(<T>::from_str(&string).map_err(|e| FromExplorerValueErr(e.into()))?);
                 } else {
-                    return Err(StringErr(format!("Expected array of {}.", stringify!(T))));
+                    return Err(FromExplorerValueErr(
+                        format!("Expected array of {}.", stringify!(T)).into(),
+                    ));
                 }
             }
             Ok(vec)
         } else {
-            Err(StringErr(format!("Expected array of {}.", stringify!(T))))
+            Err(FromExplorerValueErr(
+                format!("Expected array of {}.", stringify!(T)).into(),
+            ))
         }
     }
 
@@ -148,23 +160,23 @@ macro_rules! impl_explorable_value_for_smallvec {
         impl<T, E> ExplorableValue for SmallVec<[T; $N]>
         where
             T: Display + FromStr<Err = E>,
-            E: Display,
+            E: Error + Send + Sync + 'static,
         {
-            fn from_explorer_str(source: &str) -> Result<SmallVec<[T; $N]>, StringErr> {
+            fn from_explorer_str(source: &str) -> Result<SmallVec<[T; $N]>, FromExplorerValueErr> {
                 if let serde_json::Value::Array(json_array) =
-                    serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
+                    serde_json::Value::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))?
                 {
                     let mut svec = SmallVec::with_capacity(json_array.len());
                     for value in json_array {
                         if let serde_json::Value::String(string) = value {
-                            svec.push(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?);
+                            svec.push(<T>::from_str(&string).map_err(|e| FromExplorerValueErr(e.into()))?);
                         } else {
-                            return Err(StringErr(format!("Expected array of {}.", stringify!(T))));
+                            return Err(FromExplorerValueErr(format!("Expected array of {}.", stringify!(T)).into()));
                         }
                     }
                     Ok(svec)
                 } else {
-                    Err(StringErr(format!("Expected array of {}.", stringify!(T))))
+                    Err(FromExplorerValueErr(format!("Expected array of {}.", stringify!(T)).into()))
                 }
             }
 
@@ -183,23 +195,29 @@ impl_explorable_value_for_smallvec!(2, 4, 8, 16, 32, 64);
 impl<T, E> ExplorableValue for BTreeSet<T>
 where
     T: Display + FromStr<Err = E> + Ord,
-    E: Display,
+    E: Error + Send + Sync + 'static,
 {
-    fn from_explorer_str(source: &str) -> Result<BTreeSet<T>, StringErr> {
+    fn from_explorer_str(source: &str) -> Result<BTreeSet<T>, FromExplorerValueErr> {
         if let serde_json::Value::Array(json_array) =
-            serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
+            serde_json::Value::from_str(source).map_err(|e| FromExplorerValueErr(e.into()))?
         {
             let mut bt_set = BTreeSet::new();
             for value in json_array {
                 if let serde_json::Value::String(string) = value {
-                    bt_set.insert(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?);
+                    bt_set.insert(
+                        <T>::from_str(&string).map_err(|e| FromExplorerValueErr(e.into()))?,
+                    );
                 } else {
-                    return Err(StringErr(format!("Expected array of {}.", stringify!(T))));
+                    return Err(FromExplorerValueErr(
+                        format!("Expected array of {}.", stringify!(T)).into(),
+                    ));
                 }
             }
             Ok(bt_set)
         } else {
-            Err(StringErr(format!("Expected array of {}.", stringify!(T))))
+            Err(FromExplorerValueErr(
+                format!("Expected array of {}.", stringify!(T)).into(),
+            ))
         }
     }
 
@@ -255,12 +273,26 @@ pub enum ExplorerActionResponse {
     DeleteOk,
 }
 
+#[derive(Debug, Error)]
+#[error("Fail to exec explorer action: {0}")]
+pub struct ExplorerActionErr(pub DynErr);
+impl From<FromExplorerKeyErr> for ExplorerActionErr {
+    fn from(e: FromExplorerKeyErr) -> Self {
+        ExplorerActionErr(e.0)
+    }
+}
+impl From<FromExplorerValueErr> for ExplorerActionErr {
+    fn from(e: FromExplorerValueErr) -> Self {
+        ExplorerActionErr(e.0)
+    }
+}
+
 impl<'a> ExplorerAction<'a> {
     pub fn exec<BC: BackendCol, E: EventTrait>(
         self,
         col: &ColRw<BC, E>,
         stringify_json_value: fn(serde_json::Value) -> serde_json::Value,
-    ) -> KvResult<Result<ExplorerActionResponse, StringErr>> {
+    ) -> KvResult<Result<ExplorerActionResponse, ExplorerActionErr>> {
         Ok(match self {
             Self::Count => Ok(ExplorerActionResponse::Count(col.to_ro().count()?)),
             Self::Get { key } => match E::K::from_explorer_str(key) {
@@ -270,7 +302,7 @@ impl<'a> ExplorerAction<'a> {
                         .map(|v| v.to_explorer_json())
                         .transpose()?,
                 )),
-                Err(e) => Err(e),
+                Err(e) => Err(e.into()),
             },
             Self::Find {
                 key_min,
@@ -323,7 +355,7 @@ impl<'a> ExplorerAction<'a> {
                         stringify_json_value,
                     )?,
                 })),
-                Err(e) => Err(e),
+                Err(e) => Err(ExplorerActionErr(e)),
             },
             Self::Put { key, value } => match E::K::from_explorer_str(key) {
                 Ok(k) => match E::V::from_explorer_str(value) {
@@ -331,16 +363,16 @@ impl<'a> ExplorerAction<'a> {
                         col.upsert(k, v)?;
                         Ok(ExplorerActionResponse::PutOk)
                     }
-                    Err(e) => Err(e),
+                    Err(e) => Err(e.into()),
                 },
-                Err(e) => Err(e),
+                Err(e) => Err(e.into()),
             },
             Self::Delete { key } => match E::K::from_explorer_str(key) {
                 Ok(k) => {
                     col.remove(k)?;
                     Ok(ExplorerActionResponse::DeleteOk)
                 }
-                Err(e) => Err(e),
+                Err(e) => Err(e.into()),
             },
         })
     }
@@ -428,7 +460,7 @@ enum Range<K> {
 fn define_range<K: Key>(
     key_min_opt: Option<String>,
     key_max_opt: Option<String>,
-) -> Result<Range<K>, StringErr> {
+) -> Result<Range<K>, DynErr> {
     if let Some(key_min) = key_min_opt {
         let k_min = K::from_explorer_str(&key_min)?;
         if let Some(key_max) = key_max_opt {
diff --git a/rust-libs/tools/kv_typed/src/from_bytes.rs b/rust-libs/tools/kv_typed/src/from_bytes.rs
index c3c39541a..61c0c02d5 100644
--- a/rust-libs/tools/kv_typed/src/from_bytes.rs
+++ b/rust-libs/tools/kv_typed/src/from_bytes.rs
@@ -1,7 +1,11 @@
 use crate::*;
 
+#[derive(Clone, Copy, Debug, Error)]
+#[error("Corrupted DB: {0} bytes are wrong aligned or have invalid length")]
+pub struct LayoutVerifiedErr(pub &'static str);
+
 pub trait FromBytes: Sized {
-    type Err: Error;
+    type Err: Error + Send + Sync + 'static;
 
     /// Create Self from bytes.
     fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err>;
@@ -57,11 +61,11 @@ macro_rules! impl_from_bytes_for_smallvec {
         where
             T: Copy + zerocopy::FromBytes,
         {
-            type Err = StringErr;
+            type Err = LayoutVerifiedErr;
 
             fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
                 let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
-                    .ok_or_else(|| StringErr("".to_owned()))?;
+                    .ok_or_else(|| LayoutVerifiedErr(stringify!(T)).into())?;
                 Ok(SmallVec::from_slice(layout_verified.into_slice()))
             }
         }
@@ -73,16 +77,11 @@ impl<T> FromBytes for Vec<T>
 where
     T: Copy + Default + zerocopy::FromBytes,
 {
-    type Err = StringErr;
+    type Err = LayoutVerifiedErr;
 
     fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
-        let layout_verified =
-            zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes).ok_or_else(|| {
-                StringErr(
-                    "Corrupted DB: Vec<T> bytes are wrong aligned or have invalid length"
-                        .to_owned(),
-                )
-            })?;
+        let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
+            .ok_or(LayoutVerifiedErr(stringify!(Vec<T>)))?;
         let slice = layout_verified.into_slice();
         let mut vec = Vec::with_capacity(slice.len());
         vec.resize_with(slice.len(), Default::default);
@@ -95,11 +94,11 @@ impl<T> FromBytes for BTreeSet<T>
 where
     T: Copy + zerocopy::FromBytes + Ord,
 {
-    type Err = StringErr;
+    type Err = LayoutVerifiedErr;
 
     fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
         let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
-            .ok_or_else(|| StringErr("Bytes are invalid length or alignment.".to_owned()))?;
+            .ok_or(LayoutVerifiedErr(stringify!(BTreeSet<T>)))?;
         let slice = layout_verified.into_slice();
         Ok(BTreeSet::from_iter(slice.iter().copied()))
     }
@@ -109,11 +108,11 @@ impl<T> FromBytes for HashSet<T>
 where
     T: Copy + Eq + zerocopy::FromBytes + std::hash::Hash,
 {
-    type Err = StringErr;
+    type Err = LayoutVerifiedErr;
 
     fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Err> {
         let layout_verified = zerocopy::LayoutVerified::<_, [T]>::new_slice(bytes)
-            .ok_or_else(|| StringErr("".to_owned()))?;
+            .ok_or(LayoutVerifiedErr(stringify!(HashSet<T>)))?;
         let slice = layout_verified.into_slice();
         Ok(HashSet::from_iter(slice.iter().copied()))
     }
diff --git a/rust-libs/tools/kv_typed/src/iter.rs b/rust-libs/tools/kv_typed/src/iter.rs
index 7e1256df0..8671cb182 100644
--- a/rust-libs/tools/kv_typed/src/iter.rs
+++ b/rust-libs/tools/kv_typed/src/iter.rs
@@ -62,9 +62,9 @@ impl<C: BackendCol, KB: KeyBytes, VB: ValueBytes, BI: BackendIter<KB, VB>, K: Ke
             Some(Ok((key_bytes, value_bytes))) => match K::from_bytes(key_bytes.as_ref()) {
                 Ok(key) => match V::from_bytes(value_bytes.as_ref()) {
                     Ok(value) => Some(Ok((key, value))),
-                    Err(e) => Some(Err(KvError::DeserError(format!("{}", e)))),
+                    Err(e) => Some(Err(KvError::DeserError(e.into()))),
                 },
-                Err(e) => Some(Err(KvError::DeserError(format!("{}", e)))),
+                Err(e) => Some(Err(KvError::DeserError(e.into()))),
             },
             Some(Err(e)) => Some(Err(KvError::BackendError(e))),
             None => None,
diff --git a/rust-libs/tools/kv_typed/src/iter/keys.rs b/rust-libs/tools/kv_typed/src/iter/keys.rs
index 0555e16ce..4e2cf10d2 100644
--- a/rust-libs/tools/kv_typed/src/iter/keys.rs
+++ b/rust-libs/tools/kv_typed/src/iter/keys.rs
@@ -33,7 +33,7 @@ impl<C: BackendCol, KB: KeyBytes, VB: ValueBytes, BI: BackendIter<KB, VB>, K: Ke
         match self.backend_iter.next() {
             Some(Ok((key_bytes, _value_bytes))) => match K::from_bytes(key_bytes.as_ref()) {
                 Ok(key) => Some(Ok(key)),
-                Err(e) => Some(Err(KvError::DeserError(format!("{}", e)))),
+                Err(e) => Some(Err(KvError::DeserError(e.into()))),
             },
             Some(Err(e)) => Some(Err(KvError::BackendError(e))),
             None => None,
diff --git a/rust-libs/tools/kv_typed/src/iter/values.rs b/rust-libs/tools/kv_typed/src/iter/values.rs
index 8652d53c4..a96f608df 100644
--- a/rust-libs/tools/kv_typed/src/iter/values.rs
+++ b/rust-libs/tools/kv_typed/src/iter/values.rs
@@ -39,7 +39,7 @@ impl<C: BackendCol, KB: KeyBytes, VB: ValueBytes, BI: BackendIter<KB, VB>, K: Ke
         match self.backend_iter.next() {
             Some(Ok((_key_bytes, value_bytes))) => match V::from_bytes(value_bytes.as_ref()) {
                 Ok(value) => Some(Ok(value)),
-                Err(e) => Some(Err(KvError::DeserError(format!("{}", e)))),
+                Err(e) => Some(Err(KvError::DeserError(e.into()))),
             },
             Some(Err(e)) => Some(Err(KvError::BackendError(e))),
             None => None,
diff --git a/rust-libs/tools/kv_typed/src/lib.rs b/rust-libs/tools/kv_typed/src/lib.rs
index e22917afe..d855b35b5 100644
--- a/rust-libs/tools/kv_typed/src/lib.rs
+++ b/rust-libs/tools/kv_typed/src/lib.rs
@@ -75,11 +75,13 @@ pub mod prelude {
         DbCollectionRoIterRefSlice,
     };
     pub use crate::collection_rw::{ColRw, DbCollectionRw};
-    pub use crate::error::{DynErr, KvError, KvResult, StringErr};
+    pub use crate::error::{DynErr, KvError, KvResult};
     pub use crate::event::{EventTrait, Events};
     #[cfg(feature = "explorer")]
-    pub use crate::explorer::{ExplorableKey, ExplorableValue};
-    pub use crate::from_bytes::FromBytes;
+    pub use crate::explorer::{
+        ExplorableKey, ExplorableValue, ExplorerActionErr, FromExplorerKeyErr, FromExplorerValueErr,
+    };
+    pub use crate::from_bytes::{FromBytes, LayoutVerifiedErr};
     pub use crate::iter::{
         keys::KvIterKeys, values::KvIterValues, EntryIter, KvIter, KvIterRefSlice, ResultIter,
     };
diff --git a/rust-libs/tools/kv_typed/src/transactional_write.rs b/rust-libs/tools/kv_typed/src/transactional_write.rs
index 7f461bc49..822d07242 100644
--- a/rust-libs/tools/kv_typed/src/transactional_write.rs
+++ b/rust-libs/tools/kv_typed/src/transactional_write.rs
@@ -61,7 +61,7 @@ impl<'tx, BC: BackendCol, E: EventTrait> TxColRw<'tx, BC, E> {
             batch::BatchGet::None => self.col_reader.backend_col.get(k),
             batch::BatchGet::Deleted => Ok(None),
             batch::BatchGet::Updated(v) => Ok(Some(v.as_bytes(|v_bytes| {
-                E::V::from_bytes(v_bytes).map_err(|e| KvError::DeserError(format!("{}", e)))
+                E::V::from_bytes(v_bytes).map_err(|e| KvError::DeserError(e.into()))
             })?)),
         }
     }
diff --git a/rust-libs/tools/kv_typed/src/value.rs b/rust-libs/tools/kv_typed/src/value.rs
index e55ecbf61..d2ba89b62 100644
--- a/rust-libs/tools/kv_typed/src/value.rs
+++ b/rust-libs/tools/kv_typed/src/value.rs
@@ -107,7 +107,7 @@ where
         + Sync
         + zerocopy::AsBytes
         + zerocopy::FromBytes,
-    E: Display,
+    E: Error + Send + Sync + 'static,
 {
     type Elem = T;
 
@@ -132,7 +132,7 @@ macro_rules! impl_value_slice_zc_for_smallvec {
                 + Sync
                 + zerocopy::AsBytes
                 + zerocopy::FromBytes,
-            E: Display,
+            E: Error + Send + Sync + 'static,
         {
             type Elem = T;
 
@@ -159,7 +159,7 @@ where
         + Sync
         + zerocopy::AsBytes
         + zerocopy::FromBytes,
-    E: Display,
+    E: Error + Send + Sync + 'static,
 {
     type Elem = T;
 
-- 
GitLab