diff --git a/src/commands/account.rs b/src/commands/account.rs
index b54581479a54f6b90905bbf06de01f1c9c901ee7..fa0407a3fd47b8e67b3ce4f7b5cbbff8d0ae5996 100644
--- a/src/commands/account.rs
+++ b/src/commands/account.rs
@@ -56,7 +56,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 }
 
 /// get balance
-pub async fn get_balance(data: Data) -> Result<(), anyhow::Error> {
+pub async fn get_balance(data: Data) -> Result<(), subxt::Error> {
 	let account_id = data.address();
 	let account_info = get_account_info(data.client(), &account_id).await?;
 	if let Some(account_info) = account_info {
diff --git a/src/commands/collective.rs b/src/commands/collective.rs
index 299ead7d8c36121c90428686001134a0d89b9739..d1e777f51fa4e474c1328198cbab670db0daaade 100644
--- a/src/commands/collective.rs
+++ b/src/commands/collective.rs
@@ -22,7 +22,7 @@ pub enum Subcommand {
 }
 
 /// handle technical committee commands
-pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(), GcliError> {
+pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
 	let data = data.build_client().await?.build_indexer().await?;
 	match command {
 		Subcommand::Members => technical_committee_members(&data).await?,
@@ -46,7 +46,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 }
 
 /// list technical committee members
-pub async fn technical_committee_members(data: &Data) -> Result<(), anyhow::Error> {
+pub async fn technical_committee_members(data: &Data) -> Result<(), subxt::Error> {
 	let client = data.client();
 	let indexer = &data.indexer;
 
@@ -85,7 +85,6 @@ pub async fn technical_committee_members(data: &Data) -> Result<(), anyhow::Erro
 			.unwrap_or_else(|| account_id.to_string(),)
 		);
 	}
-
 	Ok(())
 }
 
@@ -93,7 +92,7 @@ pub async fn technical_committee_members(data: &Data) -> Result<(), anyhow::Erro
 // TODO:
 // * better formatting (format pubkeys to SS58 and add usernames)
 // * display proposals indices
-pub async fn technical_committee_proposals(client: &Client) -> anyhow::Result<()> {
+pub async fn technical_committee_proposals(client: &Client) -> Result<(), subxt::Error> {
 	let parent_hash = client
 		.storage()
 		.at_latest()
@@ -112,7 +111,6 @@ pub async fn technical_committee_proposals(client: &Client) -> anyhow::Result<()
 		println!("{:#?}", item.value);
 		println!();
 	}
-
 	Ok(())
 }
 
@@ -122,7 +120,7 @@ pub async fn technical_committee_vote(
 	proposal_hash: Hash,
 	proposal_index: u32,
 	vote: bool,
-) -> anyhow::Result<(), subxt::Error> {
+) -> Result<(), subxt::Error> {
 	submit_call_and_look_event::<
 		runtime::technical_committee::events::Voted,
 		StaticPayload<runtime::technical_committee::calls::types::Vote>,
@@ -140,7 +138,7 @@ pub async fn technical_committee_vote(
 pub async fn technical_committee_propose(
 	data: &Data,
 	proposal: &str,
-) -> anyhow::Result<(), subxt::Error> {
+) -> Result<(), subxt::Error> {
 	let raw_call = hex::decode(proposal).expect("invalid hex");
 	let call = codec::decode_from_bytes(raw_call.into()).expect("invalid call");
 	let payload = runtime::tx().technical_committee().propose(5, call, 100);
diff --git a/src/commands/expire.rs b/src/commands/expire.rs
index 9afd41d9c0c4932b50b454c4c4996cba5b227602..64aa7fc5cb4f2623bcda4f10da8122bdcd33d324 100644
--- a/src/commands/expire.rs
+++ b/src/commands/expire.rs
@@ -2,7 +2,7 @@ use crate::{indexer::*, *};
 use futures::join;
 use std::collections::BTreeMap;
 
-pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> anyhow::Result<()> {
+pub async fn monitor_expirations(data: &Data, blocks: u32, _sessions: u32) -> Result<(), subxt::Error> {
 	let client = data.client();
 	let indexer = data.indexer.clone();
 
@@ -119,7 +119,7 @@ impl IdentityCache {
 		}
 	}
 
-	pub async fn fetch_identity(&mut self, identity_id: IdtyId) -> anyhow::Result<String> {
+	pub async fn fetch_identity(&mut self, identity_id: IdtyId) -> Result<String, GcliError> {
 		Ok(match self.identities.entry(identity_id) {
 			hash_map::Entry::Occupied(entry) => entry.get().clone(),
 			hash_map::Entry::Vacant(entry) => entry
diff --git a/src/commands/identity.rs b/src/commands/identity.rs
index c3481fa5724618e3c9e86b84fc8a9cc6465bb155..f6aa480084d33015265ce26cd83b32e72ace32f8 100644
--- a/src/commands/identity.rs
+++ b/src/commands/identity.rs
@@ -257,11 +257,11 @@ pub async fn get_identity(
 			// account_id → idty_id
 			(None, Some(account_id), None) => get_idty_index_by_account_id(client, account_id)
 				.await?
-				.ok_or_else(|| anyhow!("no identity for account '{account_id}'"))?,
+				.ok_or_else(|| GcliError::Duniter(format!("no identity for account '{account_id}'")))?,
 			// pseudo → idty_id
 			(None, None, Some(pseudo)) => get_idty_index_by_name(client, pseudo)
 				.await?
-				.ok_or_else(|| anyhow!("no identity for name '{pseudo}'"))?,
+				.ok_or_else(|| GcliError::Indexer(format!("no identity for name '{pseudo}'")))?,
 			_ => {
 				return Err(GcliError::Logic(
 					"One and only one argument is needed to fetch the identity.".to_string(),
@@ -271,14 +271,14 @@ pub async fn get_identity(
 	// idty_id → value
 	let value = get_identity_by_index(client, index)
 		.await?
-		.ok_or_else(|| anyhow!("no identity value for index {index}"))?;
+		.ok_or_else(|| GcliError::Duniter(format!("no identity value for index {index}")))?;
 
 	// pseudo
 	let pseudo = pseudo.unwrap_or(if let Some(indexer) = &indexer {
 		indexer
 			.username_by_index(index)
 			.await
-			.ok_or_else(|| anyhow!("indexer does not have username for this index {index}"))?
+			.ok_or_else(|| GcliError::Indexer(format!("indexer does not have username for this index {index}")))?
 	} else {
 		"<no indexer>".to_string()
 	});
diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs
index fcf4fa1db25dacaa2484c35172564b3e7c83c812..44ffee8a3b95468a38f044ec224c34ad95bf882c 100644
--- a/src/commands/oneshot.rs
+++ b/src/commands/oneshot.rs
@@ -65,7 +65,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 }
 
 /// get balance of oneshot account
-pub async fn oneshot_account_balance(data: &Data) -> Result<(), anyhow::Error> {
+pub async fn oneshot_account_balance(data: &Data) -> Result<(), subxt::Error> {
 	println!(
 		"balance of oneshot account {} is: {}",
 		data.address(),
diff --git a/src/commands/smith.rs b/src/commands/smith.rs
index 0394392c8ac0bb44ed3275cf339e9ffd37a79a28..d1814345302917804d99a67f40f4a3680a56ab66 100644
--- a/src/commands/smith.rs
+++ b/src/commands/smith.rs
@@ -110,20 +110,20 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 
 /// rotate session keys
 /// (needs to be connected to unsafe RPC)
-pub async fn rotate_keys(data: &Data) -> Result<SessionKeys, anyhow::Error> {
+pub async fn rotate_keys(data: &Data) -> Result<SessionKeys, GcliError> {
 	data.legacy_rpc_methods()
 		.await
 		.author_rotate_keys()
 		.await
 		.map_err(|e| {
-			anyhow!(
+			GcliError::Duniter(format!(
 				"Please make sure you are connected to your validator node with the unsafe RPC \
 				 API enabled {e}"
-			)
+			))
 		})?
 		.deref()
 		.try_into()
-		.map_err(|e| anyhow!("Session keys have wrong length: {:?}", e))
+		.map_err(|e| GcliError::Duniter(format!("Session keys have wrong length: {:?}", e)))
 }
 
 /// set session keys
@@ -190,7 +190,7 @@ pub async fn go_offline(data: &Data) -> Result<(), subxt::Error> {
 }
 
 /// get online authorities
-pub async fn online(data: &Data) -> Result<(), anyhow::Error> {
+pub async fn online(data: &Data) -> Result<(), subxt::Error> {
 	let client = data.client();
 
 	let online_authorities = client
diff --git a/src/data.rs b/src/data.rs
index cfbf201f273e015227d0dcfbf8b3cdec744c12ff..d0462af28ea50b9085c6a4c4251b01fc0a655d76 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -232,11 +232,15 @@ impl Data {
 		Ok(self)
 	}
 	/// get properties
-	pub async fn fetch_system_properties(mut self) -> Result<Self, anyhow::Error> {
+	pub async fn fetch_system_properties(mut self) -> Result<Self, GcliError> {
 		let system_properties = self.legacy_rpc_methods().await.system_properties().await?;
 		let system_properties = serde_json::from_value::<SystemProperties>(
 			serde_json::Value::Object(system_properties),
-		)?;
+		)
+		.map_err(|e| {
+			dbg!(e);
+			GcliError::Duniter("could not read duniter system properties".to_string())
+		})?;
 		self.token_decimals = system_properties.token_decimals;
 		self.token_symbol = system_properties.token_symbol;
 		Ok(self)