Repository: probe-rs/probe-rs
Pull request #2676 (2024-07-11 20:20:52)

Cleanup error handling
Try to only use the `DebugProbeError`for things which are somewhat related to debug probes, replace uses of `anyhow` with simple strings where we didn't really need it.


Patch:
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/mod.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/mod.rs
index e3389978..a3bb5235 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/mod.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/mod.rs
@@ -9,8 +9,11 @@ mod test;
 
 use anyhow::Result;
 use probe_rs::{
-    architecture::arm::ap::AccessPortError, flashing::FileDownloadError, probe::list::Lister,
-    probe::DebugProbeError, CoreDumpError, Error,
+    architecture::arm::ap::AccessPortError,
+    debug::DebugError,
+    flashing::FileDownloadError,
+    probe::{list::Lister, DebugProbeError},
+    CoreDumpError, Error,
 };
 use server::startup::debug;
 use std::{
@@ -34,6 +37,8 @@ pub enum DebuggerError {
     #[error(transparent)]
     DebugProbe(#[from] DebugProbeError),
     #[error(transparent)]
+    DebugError(#[from] DebugError),
+    #[error(transparent)]
     FileDownload(#[from] FileDownloadError),
     #[error("Received an invalid requeset")]
     InvalidRequest,
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/peripherals/svd_cache.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/peripherals/svd_cache.rs
index 8485fcd0..72673935 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/peripherals/svd_cache.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/peripherals/svd_cache.rs
@@ -1,9 +1,7 @@
 use std::collections::BTreeMap;
 
-use probe_rs::debug::{get_object_reference, ObjectRef};
-use probe_rs::{Error, MemoryInterface};
-
-use anyhow::anyhow;
+use probe_rs::debug::{get_object_reference, DebugError, ObjectRef};
+use probe_rs::MemoryInterface;
 
 /// VariableCache stores available `Variable`s, and provides methods to create and navigate the parent-child relationships of the Variables.
 #[derive(Debug, Clone, PartialEq)]
@@ -106,7 +104,7 @@ impl SvdVariableCache {
         parent_key: ObjectRef,
         name: String,
         variable: SvdVariable,
-    ) -> Result<ObjectRef, Error> {
+    ) -> Result<ObjectRef, DebugError> {
         let cache_variable = {
             let variable_key = get_object_reference();
             Variable {
@@ -119,7 +117,7 @@ impl SvdVariableCache {
 
         // Validate that the parent_key exists ...
         if !self.variable_hash_map.contains_key(&parent_key) {
-            return Err(anyhow!("SvdVariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug", cache_variable.name, parent_key).into());
+            return Err(DebugError::Other(format!("SvdVariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug", cache_variable.name, parent_key)));
         }
 
         tracing::trace!(
@@ -133,7 +131,7 @@ impl SvdVariableCache {
             .variable_hash_map
             .insert(cache_variable.variable_key, cache_variable.clone())
         {
-            return Err(anyhow!("Attempt to insert a new `SvdVariable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.", cache_variable.name, old_variable.variable_key).into());
+            return Err(DebugError::Other(format!("Attempt to insert a new `SvdVariable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.", cache_variable.name, old_variable.variable_key)));
         }
 
         Ok(cache_variable.variable_key)
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs
index b3b3da17..984d9cd2 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs
@@ -25,7 +25,7 @@ use probe_rs::{
         debug_info::DebugInfo, stack_frame::StackFrameInfo, ColumnType, ObjectRef, VariableCache,
     },
     rtt::{Rtt, ScanRegion},
-    Core, CoreStatus, Error, HaltReason,
+    Core, CoreStatus, HaltReason,
 };
 use time::UtcOffset;
 use typed_path::TypedPathBuf;
@@ -78,7 +78,7 @@ impl<'p> CoreHandle<'p> {
     pub(crate) fn poll_core<P: ProtocolAdapter>(
         &mut self,
         debug_adapter: &mut DebugAdapter<P>,
-    ) -> Result<CoreStatus, Error> {
+    ) -> Result<CoreStatus, DebuggerError> {
         if debug_adapter.configuration_is_done() {
             match self.core.status() {
                 Ok(status) => {
@@ -131,7 +131,7 @@ impl<'p> CoreHandle<'p> {
                                     MessageSeverity::Error,
                                     status.short_long_status(None).1,
                                 );
-                                return Err(Error::Other(anyhow!(
+                                return Err(DebuggerError::Other(anyhow!(
                                     status.short_long_status(None).1
                                 )));
                             }
@@ -140,7 +140,7 @@ impl<'p> CoreHandle<'p> {
                                     anyhow!("Unknown Device status reveived from Probe-rs"),
                                 ))?;
 
-                                return Err(Error::Other(anyhow!(
+                                return Err(DebuggerError::Other(anyhow!(
                                     "Unknown Device status reveived from Probe-rs"
                                 )));
                             }
@@ -151,7 +151,7 @@ impl<'p> CoreHandle<'p> {
                 }
                 Err(error) => {
                     self.core_data.last_known_status = CoreStatus::Unknown;
-                    Err(error)
+                    Err(error.into())
                 }
             }
         } else {
@@ -431,7 +431,7 @@ fn try_attach_rtt(
     elf_file: &Path,
     rtt_config: &RttConfig,
     timestamp_offset: UtcOffset,
-) -> Result<RttActiveTarget, Error> {
+) -> Result<RttActiveTarget, DebuggerError> {
     let elf = std::fs::read(elf_file)
         .map_err(|error| anyhow!("Error attempting to attach to RTT: {}", error))?;
 
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs
index f29051cf..10dca1ec 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs
@@ -232,11 +232,10 @@ impl SessionData {
             };
 
             // We need to poll the core to determine its status.
-            let current_core_status = target_core.poll_core(debug_adapter).map_err(|error| {
-                let error = DebuggerError::ProbeRs(error);
-                let _ = debug_adapter.show_error_message(&error);
-                error
-            })?;
+            let current_core_status =
+                target_core.poll_core(debug_adapter).inspect_err(|error| {
+                    let _ = debug_adapter.show_error_message(error);
+                })?;
 
             // If appropriate, check for RTT data.
             if core_config.rtt_config.enabled {
diff --git a/probe-rs/Cargo.toml b/probe-rs/Cargo.toml
index 55ea928b..5af88ca4 100644
--- a/probe-rs/Cargo.toml
+++ b/probe-rs/Cargo.toml
@@ -21,7 +21,7 @@ exclude = ["tests/"]
 
 [features]
 default = ["builtin-targets", "debug"]
-gdb-server = ["dep:gdbstub"]
+gdb-server = ["dep:gdbstub", "dep:anyhow"]
 
 # Enable all built in targets.
 builtin-targets = ["dep:bincode", "dep:serde_yaml", "dep:probe-rs-target"]
@@ -38,7 +38,6 @@ debug = [
 test = []
 
 [dependencies]
-anyhow.workspace = true
 docsplay.workspace = true
 thiserror.workspace = true
 probe-rs-target.workspace = true
@@ -78,6 +77,7 @@ hexdump = { version = "0.1", optional = true }
 
 # gdb server
 gdbstub = { version = "0.7", optional = true }
+anyhow = { workspace = true, optional = true }
 
 # debug
 gimli = { version = "0.30", default-features = false, features = [
@@ -97,6 +97,7 @@ bincode = { version = "1", optional = true }
 serde_yaml = { version = "0.9", optional = true }
 
 [dev-dependencies]
+anyhow.workspace = true
 pretty_env_logger = "0.5"
 fastrand = "2.1"
 serde_json = "1"
diff --git a/probe-rs/src/architecture/arm/component/mod.rs b/probe-rs/src/architecture/arm/component/mod.rs
index 17b2e55d..4927bb1c 100644
--- a/probe-rs/src/architecture/arm/component/mod.rs
+++ b/probe-rs/src/architecture/arm/component/mod.rs
@@ -120,7 +120,7 @@ pub fn get_arm_components(
             ApInformation::MemoryAp(MemoryApInformation {
                 debug_base_address: 0,
                 ..
-            }) => Err(Error::Other(anyhow::anyhow!("AP has a base address of 0"))),
+            }) => Err(Error::Other("AP has a base address of 0".to_string())),
             ApInformation::MemoryAp(MemoryApInformation {
                 address,
                 debug_base_address,
@@ -133,7 +133,7 @@ pub fn get_arm_components(
             }
             ApInformation::Other { address, .. } => {
                 // Return an error, only possible to get Component from MemoryAP
-                Err(Error::Other(anyhow::anyhow!(
+                Err(Error::Other(format!(
                     "AP {:#x?} is not a MemoryAP, unable to get ARM component.",
                     address
                 )))
diff --git a/probe-rs/src/architecture/arm/core/armv6m.rs b/probe-rs/src/architecture/arm/core/armv6m.rs
index 8f588de1..bd94ae74 100644
--- a/probe-rs/src/architecture/arm/core/armv6m.rs
+++ b/probe-rs/src/architecture/arm/core/armv6m.rs
@@ -8,11 +8,9 @@ use crate::{
     core::{CoreRegisters, RegisterId, RegisterValue, VectorCatchCondition},
     error::Error,
     memory::valid_32bit_address,
-    probe::DebugProbeError,
     Architecture, BreakpointCause, CoreInformation, CoreInterface, CoreRegister, CoreStatus,
     CoreType, HaltReason, InstructionSet, MemoryInterface, MemoryMappedRegister,
 };
-use anyhow::Result;
 use bitfield::bitfield;
 use std::{
     mem::size_of,
@@ -295,7 +293,7 @@ impl BpCompx {
         } else if bp_val.bp_match() == 0b10 {
             Ok((bp_val.comp() << 2) | 0x2)
         } else {
-            Err(Error::Probe(DebugProbeError::Other(anyhow::anyhow!("Unsupported breakpoint comparator value {:#08x} for HW breakpoint. Breakpoint must be on half-word boundaries", bp_val.0))))
+            Err(Error::Arm(ArmError::Other(format!("Unsupported breakpoint comparator value {:#08x} for HW breakpoint. Breakpoint must be on half-word boundaries", bp_val.0) )))
         }
     }
 }
@@ -707,7 +705,8 @@ impl<'probe> CoreInterface for Armv6m<'probe> {
 
     fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
         if self.state.current_state.is_halted() {
-            super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)
+            super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)?;
+            Ok(())
         } else {
             Err(Error::Arm(ArmError::CoreNotHalted))
         }
@@ -762,7 +761,7 @@ impl<'probe> CoreInterface for Armv6m<'probe> {
         // The highest 3 bits of the address have to be zero, otherwise the breakpoint cannot
         // be set at the address.
         if addr >= 0x2000_0000 {
-            return Err(Error::Probe(DebugProbeError::Other(anyhow::anyhow!("Unsupported address {:#08x} for HW breakpoint. Breakpoint must be at address < 0x2000_0000.", addr))));
+            return Err(Error::Arm(ArmError::Other(format!("Unsupported address {:#08x} for HW breakpoint. Breakpoint must be at address < 0x2000_0000.", addr))));
         }
 
         let mut value = BpCompx(0);
diff --git a/probe-rs/src/architecture/arm/core/armv7a.rs b/probe-rs/src/architecture/arm/core/armv7a.rs
index ecc40a90..f34394d0 100644
--- a/probe-rs/src/architecture/arm/core/armv7a.rs
+++ b/probe-rs/src/architecture/arm/core/armv7a.rs
@@ -25,7 +25,6 @@ use crate::{
     Architecture, CoreInformation, CoreInterface, CoreRegister, CoreStatus, CoreType,
     InstructionSet, MemoryInterface,
 };
-use anyhow::Result;
 use std::{
     mem::size_of,
     sync::Arc,
@@ -132,9 +131,9 @@ impl<'probe> Armv7a<'probe> {
     }
 
     /// Execute an instruction
-    fn execute_instruction(&mut self, instruction: u32) -> Result<Dbgdscr, Error> {
+    fn execute_instruction(&mut self, instruction: u32) -> Result<Dbgdscr, ArmError> {
         if !self.state.current_state.is_halted() {
-            return Err(Error::Arm(ArmError::CoreNotHalted));
+            return Err(ArmError::CoreNotHalted);
         }
 
         // Enable ITR if needed
@@ -168,7 +167,7 @@ impl<'probe> Armv7a<'probe> {
 
             self.memory.write_word_32(address, dbgdrcr.into())?;
 
-            return Err(Error::Arm(Armv7aError::DataAbort.into()));
+            return Err(Armv7aError::DataAbort.into());
         }
 
         Ok(dbgdscr)
diff --git a/probe-rs/src/architecture/arm/core/armv7m.rs b/probe-rs/src/architecture/arm/core/armv7m.rs
index 2bd8e399..38b86176 100644
--- a/probe-rs/src/architecture/arm/core/armv7m.rs
+++ b/probe-rs/src/architecture/arm/core/armv7m.rs
@@ -18,10 +18,8 @@ use crate::{
     },
     error::Error,
     memory::valid_32bit_address,
-    probe::DebugProbeError,
     BreakpointCause, CoreRegister, CoreType, InstructionSet, MemoryInterface,
 };
-use anyhow::{anyhow, Result};
 use bitfield::bitfield;
 use std::{
     mem::size_of,
@@ -504,7 +502,7 @@ impl FpRev1CompX {
         } else if fp1_val.replace() == 0b10 {
             Ok((fp1_val.comp() << 2) | 0x2)
         } else {
-            Err(Error::Probe(DebugProbeError::Other(anyhow::anyhow!("Unsupported breakpoint comparator value {:#08x} for HW breakpoint. Breakpoint must be on half-word boundaries", fp1_val.0))))
+            Err(Error::Arm(ArmError::Other(format!("Unsupported breakpoint comparator value {:#08x} for HW breakpoint. Breakpoint must be on half-word boundaries", fp1_val.0))))
         }
     }
     /// Get the correct register configuration which enables
@@ -907,7 +905,8 @@ impl<'probe> CoreInterface for Armv7m<'probe> {
 
     fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
         if self.state.current_state.is_halted() {
-            super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)
+            super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)?;
+            Ok(())
         } else {
             Err(Error::Arm(ArmError::CoreNotHalted))
         }
@@ -922,9 +921,9 @@ impl<'probe> CoreInterface for Armv7m<'probe> {
             Ok(reg.num_code())
         } else {
             tracing::warn!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", reg.rev());
-            Err(Error::Probe(DebugProbeError::CommandNotSupportedByProbe {
-                command_name: "get_available_breakpoint_units",
-            }))
+            Err(
+                Error::Arm(ArmError::Other(format!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", reg.rev())))
+            )
         }
     }
 
@@ -949,7 +948,7 @@ impl<'probe> CoreInterface for Armv7m<'probe> {
                     breakpoint = FpRev2CompX::from(register_value).bpaddr() << 1;
                 } else {
                     tracing::warn!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev());
-                    return Err(Error::Other(anyhow!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev())));
+                    return Err(Error::Other(format!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev())));
                 }
                 breakpoints.push(Some(breakpoint as u64));
             } else {
@@ -979,7 +978,7 @@ impl<'probe> CoreInterface for Armv7m<'probe> {
 
         // First make sure they are asking for a breakpoint on a half-word boundary.
         if (addr & 0x1) > 0 {
-            return Err(Error::Other(anyhow!(
+            return Err(Error::Other(format!(
                 "The requested breakpoint address 0x{:08x} is not on a half-word boundary",
                 addr
             )));
@@ -995,7 +994,7 @@ impl<'probe> CoreInterface for Armv7m<'probe> {
             val = FpRev2CompX::breakpoint_configuration(addr).into();
         } else {
             tracing::warn!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev());
-            return Err(Error::Other(anyhow!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev())));
+            return Err(Error::Other(format!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev())));
         }
 
         // This is fine as FpRev1CompX and Rev2CompX are just two different
diff --git a/probe-rs/src/architecture/arm/core/armv8a.rs b/probe-rs/src/architecture/arm/core/armv8a.rs
index b0eba8db..403f935e 100644
--- a/probe-rs/src/architecture/arm/core/armv8a.rs
+++ b/probe-rs/src/architecture/arm/core/armv8a.rs
@@ -21,7 +21,6 @@ use crate::{
     Architecture, CoreInformation, CoreInterface, CoreRegister, CoreStatus, CoreType,
     InstructionSet, MemoryInterface,
 };
-use anyhow::Result;
 use std::{
     sync::Arc,
     time::{Duration, Instant},
diff --git a/probe-rs/src/architecture/arm/core/armv8m.rs b/probe-rs/src/architecture/arm/core/armv8m.rs
index edd15c8d..83c5d45c 100644
--- a/probe-rs/src/architecture/arm/core/armv8m.rs
+++ b/probe-rs/src/architecture/arm/core/armv8m.rs
@@ -18,7 +18,6 @@ use crate::{
     Architecture, BreakpointCause, CoreInformation, CoreInterface, CoreRegister, CoreStatus,
     CoreType, HaltReason, InstructionSet, MemoryInterface, MemoryMappedRegister,
 };
-use anyhow::Result;
 use bitfield::bitfield;
 use std::{
     mem::size_of,
@@ -331,7 +330,9 @@ impl<'probe> CoreInterface for Armv8m<'probe> {
 
     fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
         if self.state.current_state.is_halted() {
-            super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)
+            super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)?;
+
+            Ok(())
         } else {
             Err(Error::Arm(ArmError::CoreNotHalted))
         }
diff --git a/probe-rs/src/architecture/arm/core/cortex_m.rs b/probe-rs/src/architecture/arm/core/cortex_m.rs
index de542f64..a34e5b0e 100644
--- a/probe-rs/src/architecture/arm/core/cortex_m.rs
+++ b/probe-rs/src/architecture/arm/core/cortex_m.rs
@@ -151,7 +151,7 @@ impl IdPfr1 {
     }
 }
 
-pub(crate) fn read_core_reg(memory: &mut dyn ArmProbe, addr: RegisterId) -> Result<u32, Error> {
+pub(crate) fn read_core_reg(memory: &mut dyn ArmProbe, addr: RegisterId) -> Result<u32, ArmError> {
     // Write the DCRSR value to select the register we want to read.
     let mut dcrsr_val = Dcrsr(0);
     dcrsr_val.set_regwnr(false); // Perform a read.
@@ -170,7 +170,7 @@ pub(crate) fn write_core_reg(
     memory: &mut dyn ArmProbe,
     addr: RegisterId,
     value: u32,
-) -> Result<(), Error> {
+) -> Result<(), ArmError> {
     memory.write_word_32(Dcrdr::get_mmio_address(), value)?;
 
     // write the DCRSR value to select the register we want to write.
diff --git a/probe-rs/src/architecture/arm/mod.rs b/probe-rs/src/architecture/arm/mod.rs
index 25493d7f..6ac6f6fc 100644
--- a/probe-rs/src/architecture/arm/mod.rs
+++ b/probe-rs/src/architecture/arm/mod.rs
@@ -19,7 +19,7 @@ use self::{
     sequences::ArmDebugSequenceError,
     {armv7a::Armv7aError, armv8a::Armv8aError},
 };
-use crate::probe::DebugProbeError;
+use crate::{core::memory_mapped_registers::RegisterAddressOutOfBounds, probe::DebugProbeError};
 pub use communication_interface::{
     ApInformation, ArmChipInfo, ArmCommunicationInterface, ArmProbeInterface, DapError,
     MemoryApInformation, Register,
@@ -156,9 +156,17 @@ pub enum ArmError {
     #[error("The operation requires the following extension(s): {0:?}.")]
     ExtensionRequired(&'static [&'static str]),
 
-    /// Some other error occurred.
-    #[error(transparent)]
-    Other(#[from] anyhow::Error),
+    /// An error occured while calculating the address of a register.
+    #[error("Error calculating register address.")]
+    RegisterAddressOutOfBounds(#[from] RegisterAddressOutOfBounds),
+
+    /// Some required functionality is not implemented.
+    #[error("Not implemented: {0}")]
+    NotImplemented(&'static str),
+
+    /// Another ARM error occured
+    #[error("{0}")]
+    Other(String),
 }
 
 impl ArmError {
diff --git a/probe-rs/src/architecture/arm/sequences.rs b/probe-rs/src/architecture/arm/sequences.rs
index 816d1d7c..4aff7d5d 100644
--- a/probe-rs/src/architecture/arm/sequences.rs
+++ b/probe-rs/src/architecture/arm/sequences.rs
@@ -939,9 +939,9 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
                     target_id,
                     dlpidr
                 );
-                return Err(ArmError::Other(anyhow::anyhow!(
-                    "Target ID and DLPIDR do not match, failed to select debug port"
-                )));
+                return Err(ArmError::Other(
+                    "Target ID and DLPIDR do not match, failed to select debug port".to_string(),
+                ));
             }
         }
 
@@ -982,10 +982,7 @@ pub trait DebugEraseSequence: Send + Sync {
     /// Some devices require the probe to be disconnected and re-attached after a successful chip-erase in
     /// which case it will return `Error::Probe(DebugProbeError::ReAttachRequired)`
     fn erase_all(&self, _interface: &mut dyn ArmProbeInterface) -> Result<(), ArmError> {
-        Err(DebugProbeError::NotImplemented {
-            function_name: "erase_all",
-        }
-        .into())
+        Err(ArmError::NotImplemented("erase_all"))
     }
 }
 
diff --git a/probe-rs/src/architecture/riscv/mod.rs b/probe-rs/src/architecture/riscv/mod.rs
index fbdb00da..ead04bbb 100644
--- a/probe-rs/src/architecture/riscv/mod.rs
+++ b/probe-rs/src/architecture/riscv/mod.rs
@@ -12,7 +12,6 @@ use crate::{
     CoreInterface, CoreRegister, CoreStatus, CoreType, Error, HaltReason, InstructionSet,
     MemoryInterface, MemoryMappedRegister, SemihostingCommand,
 };
-use anyhow::{anyhow, Result};
 use bitfield::bitfield;
 use communication_interface::{AbstractCommandErrorKind, RiscvCommunicationInterface, RiscvError};
 use registers::{FP, RA, RISCV_CORE_REGSISTERS, SP};
@@ -243,10 +242,9 @@ impl<'state> CoreInterface for Riscv32<'state> {
         } else if status.allrunning() {
             Ok(CoreStatus::Running)
         } else {
-            Err(
-                anyhow!("Some cores are running while some are halted, this should not happen.")
-                    .into(),
-            )
+            Err(Error::Other(
+                "Some cores are running while some are halted, this should not happen.".to_string(),
+            ))
         }
     }
 
diff --git a/probe-rs/src/architecture/xtensa/communication_interface.rs b/probe-rs/src/architecture/xtensa/communication_interface.rs
index 546f7b2d..9d028f5a 100644
--- a/probe-rs/src/architecture/xtensa/communication_interface.rs
+++ b/probe-rs/src/architecture/xtensa/communication_interface.rs
@@ -39,15 +39,6 @@ pub enum XtensaError {
     BatchedResultNotAvailable,
 }
 
-impl From<XtensaError> for DebugProbeError {
-    fn from(e: XtensaError) -> DebugProbeError {
-        match e {
-            XtensaError::DebugProbe(err) => err,
-            other_error => DebugProbeError::Other(other_error.into()),
-        }
-    }
-}
-
 impl From<XtensaError> for ProbeRsError {
     fn from(err: XtensaError) -> Self {
         match err {
@@ -157,8 +148,8 @@ impl<'probe> XtensaCommunicationInterface<'probe> {
     }
 
     /// Read the targets IDCODE.
-    pub fn read_idcode(&mut self) -> Result<u32, DebugProbeError> {
-        Ok(self.xdm.read_idcode()?)
+    pub fn read_idcode(&mut self) -> Result<u32, XtensaError> {
+        self.xdm.read_idcode()
     }
 
     /// Enter debug mode.
@@ -729,7 +720,7 @@ impl<'probe> MemoryInterface for XtensaCommunicationInterface<'probe> {
         false
     }
 
-    fn read_word_64(&mut self, address: u64) -> anyhow::Result<u64, crate::Error> {
+    fn read_word_64(&mut self, address: u64) -> Result<u64, crate::Error> {
         let mut out = [0; 8];
         self.read(address, &mut out)?;
 
@@ -750,25 +741,25 @@ impl<'probe> MemoryInterface for XtensaCommunicationInterface<'probe> {
         Ok(u16::from_le_bytes(out))
     }
 
-    fn read_word_8(&mut self, address: u64) -> anyhow::Result<u8, crate::Error> {
+    fn read_word_8(&mut self, address: u64) -> Result<u8, crate::Error> {
         let mut out = 0;
         self.read(address, std::slice::from_mut(&mut out))?;
         Ok(out)
     }
 
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> anyhow::Result<(), crate::Error> {
+    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::Error> {
         self.read_8(address, as_bytes_mut(data))
     }
 
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> anyhow::Result<(), crate::Error> {
+    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
         self.read_8(address, as_bytes_mut(data))
     }
 
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> anyhow::Result<(), crate::Error> {
+    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), crate::Error> {
         self.read_8(address, as_bytes_mut(data))
     }
 
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> anyhow::Result<(), crate::Error> {
+    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
         self.read(address, data)
     }
 
@@ -778,43 +769,43 @@ impl<'probe> MemoryInterface for XtensaCommunicationInterface<'probe> {
         Ok(())
     }
 
-    fn write_word_64(&mut self, address: u64, data: u64) -> anyhow::Result<(), crate::Error> {
+    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), crate::Error> {
         self.write(address, &data.to_le_bytes())
     }
 
-    fn write_word_32(&mut self, address: u64, data: u32) -> anyhow::Result<(), crate::Error> {
+    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), crate::Error> {
         self.write(address, &data.to_le_bytes())
     }
 
-    fn write_word_16(&mut self, address: u64, data: u16) -> anyhow::Result<(), crate::Error> {
+    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), crate::Error> {
         self.write(address, &data.to_le_bytes())
     }
 
-    fn write_word_8(&mut self, address: u64, data: u8) -> anyhow::Result<(), crate::Error> {
+    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), crate::Error> {
         self.write(address, &[data])
     }
 
-    fn write_64(&mut self, address: u64, data: &[u64]) -> anyhow::Result<(), crate::Error> {
+    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), crate::Error> {
         self.write_8(address, as_bytes(data))
     }
 
-    fn write_32(&mut self, address: u64, data: &[u32]) -> anyhow::Result<(), crate::Error> {
+    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), crate::Error> {
         self.write_8(address, as_bytes(data))
     }
 
-    fn write_16(&mut self, address: u64, data: &[u16]) -> anyhow::Result<(), crate::Error> {
+    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), crate::Error> {
         self.write_8(address, as_bytes(data))
     }
 
-    fn write_8(&mut self, address: u64, data: &[u8]) -> anyhow::Result<(), crate::Error> {
+    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), crate::Error> {
         self.write(address, data)
     }
 
-    fn supports_8bit_transfers(&self) -> anyhow::Result<bool, crate::Error> {
+    fn supports_8bit_transfers(&self) -> Result<bool, crate::Error> {
         Ok(true)
     }
 
-    fn flush(&mut self) -> anyhow::Result<(), crate::Error> {
+    fn flush(&mut self) -> Result<(), crate::Error> {
         Ok(())
     }
 }
diff --git a/probe-rs/src/core.rs b/probe-rs/src/core.rs
index 9e155504..78baf6a5 100644
--- a/probe-rs/src/core.rs
+++ b/probe-rs/src/core.rs
@@ -7,7 +7,6 @@ use crate::{
     error::Error,
     CoreType, InstructionSet, MemoryInterface, Target,
 };
-use anyhow::anyhow;
 pub use probe_rs_target::{Architecture, CoreAccessOptions};
 use probe_rs_target::{
     ArmCoreAccessOptions, MemoryRegion, RiscvCoreAccessOptions, XtensaCoreAccessOptions,
@@ -484,7 +483,9 @@ impl<'probe> Core<'probe> {
                 next_available_hw_breakpoint += 1;
             }
         }
-        Err(Error::Other(anyhow!("No available hardware breakpoints")))
+        Err(Error::Other(
+            "No available hardware breakpoints".to_string(),
+        ))
     }
 
     /// Set a hardware breakpoint
@@ -544,7 +545,7 @@ impl<'probe> Core<'probe> {
                 self.inner.clear_hw_breakpoint(bp_position)?;
                 Ok(())
             }
-            None => Err(Error::Other(anyhow!(
+            None => Err(Error::Other(format!(
                 "No breakpoint found at address {:#010x}",
                 address
             ))),
diff --git a/probe-rs/src/core/dump.rs b/probe-rs/src/core/dump.rs
index dc84c283..1bd8c286 100644
--- a/probe-rs/src/core/dump.rs
+++ b/probe-rs/src/core/dump.rs
@@ -15,14 +15,12 @@ use crate::{
     Core, CoreType, Error, InstructionSet, MemoryInterface,
 };
 use crate::{RegisterId, RegisterValue};
-use anyhow::anyhow;
 use probe_rs_target::MemoryRange;
-use scroll::Pread;
+use scroll::Cread;
 use serde::{Deserialize, Serialize};
 use std::{
     collections::HashMap,
     fs::OpenOptions,
-    mem::size_of_val,
     ops::Range,
     path::{Path, PathBuf},
 };
@@ -195,35 +193,31 @@ impl CoreDump {
         &self,
         address: u64,
         size_in_bytes: u64,
-    ) -> Result<(u64, &Vec<u8>), crate::Error> {
+    ) -> Result<&[u8], crate::Error> {
         for (range, memory) in &self.data {
             if range.contains_range(&(address..(address + size_in_bytes))) {
-                return Ok((range.start, memory));
+                let offset = (address - range.start) as usize;
+
+                return Ok(&memory[offset..][..size_in_bytes as usize]);
             }
         }
         // If we get here, then no range with the requested memory address and size was found.
-        Err(crate::Error::Other(anyhow!("The coredump does not include the memory for address {address:#x} of size {size_in_bytes:#x}")))
+        Err(crate::Error::Other(format!("The coredump does not include the memory for address {address:#x} of size {size_in_bytes:#x}")))
     }
 
     /// Read the requested memory range from the coredump, and return the data in the requested buffer.
     /// The word-size of the read is determined by the size of the items in the `data` buffer.
-    fn read_memory_range<'a, T>(
-        &'a self,
-        address: u64,
-        data: &'a mut [T],
-    ) -> Result<(), crate::Error>
+    fn read_memory_range<T>(&self, address: u64, data: &mut [T]) -> Result<(), crate::Error>
     where
-        <T as scroll::ctx::TryFromCtx<'a, scroll::Endian>>::Error:
-            std::convert::From<scroll::Error>,
-        <T as scroll::ctx::TryFromCtx<'a, scroll::Endian>>::Error: std::fmt::Display,
-        T: scroll::ctx::TryFromCtx<'a, scroll::Endian>,
+        T: scroll::ctx::FromCtx<scroll::Endian>,
     {
-        let (memory_offset, memory) =
-            self.get_memory_from_coredump(address, (size_of_val(data)) as u64)?;
+        let memory =
+            self.get_memory_from_coredump(address, (std::mem::size_of_val(data)) as u64)?;
+
+        let value_size = std::mem::size_of::<T>();
+
         for (n, data) in data.iter_mut().enumerate() {
-            *data = memory
-                .pread_with::<T>((address - memory_offset) as usize + n * 4, scroll::LE)
-                .map_err(|e| anyhow!("{e}"))?;
+            *data = memory.cread_with::<T>(n * value_size, scroll::LE);
         }
         Ok(())
     }
diff --git a/probe-rs/src/core/memory_mapped_registers.rs b/probe-rs/src/core/memory_mapped_registers.rs
index 3813f0ef..823171d6 100644
--- a/probe-rs/src/core/memory_mapped_registers.rs
+++ b/probe-rs/src/core/memory_mapped_registers.rs
@@ -1,3 +1,11 @@
+#[derive(Debug, thiserror::Error)]
+#[error("Overflow while attempting to determine the MMIO address for register {register} at offset {offset:#x} from base address {base_address:#x}")]
+pub struct RegisterAddressOutOfBounds {
+    register: &'static str,
+    base_address: u64,
+    offset: u64,
+}
+
 /// A memory mapped register, for instance ARM debug registers (DHCSR, etc).
 pub trait MemoryMappedRegister<T>: Clone + From<T> + Into<T> + Sized + std::fmt::Debug {
     /// The register's address in the target memory.
@@ -9,11 +17,15 @@ pub trait MemoryMappedRegister<T>: Clone + From<T> + Into<T> + Sized + std::fmt:
     /// Get the register's address in the memory map.
     /// For architectures like ARM Cortex-A, this address is offset from a base address, which must be supplied when calling this method.
     /// For others (e.g. ARM Cortex-M, RISC-V) where this address is a constant, please use [`MemoryMappedRegister::get_mmio_address`].
-    fn get_mmio_address_from_base(base_address: u64) -> Result<u64, anyhow::Error> {
+    fn get_mmio_address_from_base(base_address: u64) -> Result<u64, RegisterAddressOutOfBounds> {
         if let Some(mmio_address) = base_address.checked_add(Self::ADDRESS_OFFSET) {
             Ok(mmio_address)
         } else {
-            Err(anyhow::anyhow!("Overflow while attempting to determine the MMIO address for register {} at offset {:#x} from base address {:#x}", Self::NAME, Self::ADDRESS_OFFSET, base_address))
+            Err(RegisterAddressOutOfBounds {
+                register: Self::NAME,
+                base_address,
+                offset: Self::ADDRESS_OFFSET,
+            })
         }
     }
     /// Get the register's address in the memory map.
diff --git a/probe-rs/src/core/registers.rs b/probe-rs/src/core/registers.rs
index 13e1b473..33debe31 100644
--- a/probe-rs/src/core/registers.rs
+++ b/probe-rs/src/core/registers.rs
@@ -1,7 +1,6 @@
 //! Core registers are represented by the `CoreRegister` struct, and collected in a `RegisterFile` for each of the supported architectures.
 
 use crate::Error;
-use anyhow::{anyhow, Result};
 use serde::{Deserialize, Serialize};
 use std::{
     cmp::Ordering,
@@ -248,10 +247,9 @@ impl RegisterValue {
                     *value = reg_val;
                     Ok(())
                 } else {
-                    Err(Error::Other(anyhow!(
+                    Err(Error::Other(format!(
                         "Overflow error: Attempting to add {} bytes to Register value {}",
-                        bytes,
-                        self
+                        bytes, self
                     )))
                 }
             }
@@ -260,10 +258,9 @@ impl RegisterValue {
                     *value = reg_val;
                     Ok(())
                 } else {
-                    Err(Error::Other(anyhow!(
+                    Err(Error::Other(format!(
                         "Overflow error: Attempting to add {} bytes to Register value {}",
-                        bytes,
-                        self
+                        bytes, self
                     )))
                 }
             }
@@ -272,10 +269,9 @@ impl RegisterValue {
                     *value = reg_val;
                     Ok(())
                 } else {
-                    Err(Error::Other(anyhow!(
+                    Err(Error::Other(format!(
                         "Overflow error: Attempting to add {} bytes to Register value {}",
-                        bytes,
-                        self
+                        bytes, self
                     )))
                 }
             }
@@ -290,10 +286,9 @@ impl RegisterValue {
                     *value = reg_val;
                     Ok(())
                 } else {
-                    Err(Error::Other(anyhow!(
+                    Err(Error::Other(format!(
                         "Overflow error: Attempting to subtract {} bytes to Register value {}",
-                        bytes,
-                        self
+                        bytes, self
                     )))
                 }
             }
@@ -302,10 +297,9 @@ impl RegisterValue {
                     *value = reg_val;
                     Ok(())
                 } else {
-                    Err(Error::Other(anyhow!(
+                    Err(Error::Other(format!(
                         "Overflow error: Attempting to subtract {} bytes to Register value {}",
-                        bytes,
-                        self
+                        bytes, self
                     )))
                 }
             }
@@ -314,10 +308,9 @@ impl RegisterValue {
                     *value = reg_val;
                     Ok(())
                 } else {
-                    Err(Error::Other(anyhow!(
+                    Err(Error::Other(format!(
                         "Overflow error: Attempting to subtract {} bytes to Register value {}",
-                        bytes,
-                        self
+                        bytes, self
                     )))
                 }
             }
@@ -417,10 +410,10 @@ impl TryInto<u32> for RegisterValue {
             Self::U32(v) => Ok(v),
             Self::U64(v) => v
                 .try_into()
-                .map_err(|_| crate::Error::Other(anyhow!("Value '{}' too large for u32", v))),
+                .map_err(|_| crate::Error::Other(format!("Value '{}' too large for u32", v))),
             Self::U128(v) => v
                 .try_into()
-                .map_err(|_| crate::Error::Other(anyhow!("Value '{}' too large for u32", v))),
+                .map_err(|_| crate::Error::Other(format!("Value '{}' too large for u32", v))),
         }
     }
 }
@@ -434,7 +427,7 @@ impl TryInto<u64> for RegisterValue {
             Self::U64(v) => Ok(v),
             Self::U128(v) => v
                 .try_into()
-                .map_err(|_| crate::Error::Other(anyhow!("Value '{}' too large for u64", v))),
+                .map_err(|_| crate::Error::Other(format!("Value '{}' too large for u64", v))),
         }
     }
 }
diff --git a/probe-rs/src/debug/debug_info.rs b/probe-rs/src/debug/debug_info.rs
index ea37259f..f074b84e 100644
--- a/probe-rs/src/debug/debug_info.rs
+++ b/probe-rs/src/debug/debug_info.rs
@@ -12,9 +12,8 @@ use crate::{
         registers, stack_frame::StackFrameInfo, unit_info::RangeExt, SourceLocation,
         VerifiedBreakpoint,
     },
-    MemoryInterface,
+    Error, MemoryInterface,
 };
-use anyhow::anyhow;
 use gimli::{
     BaseAddresses, DebugFrame, DebugInfoOffset, UnwindContext, UnwindSection, UnwindTableRow,
 };
@@ -289,7 +288,7 @@ impl DebugInfo {
 
                 let Some(unit_info) = unit_infos.next() else {
                     // No unit infos
-                    return Err(DebugError::Other(anyhow::anyhow!("Missing unit infos")));
+                    return Err(DebugError::Other("Missing unit infos".to_string()));
                 };
 
                 let mut entries = unit_info.unit.entries();
@@ -917,7 +916,7 @@ impl DebugInfo {
                 return Ok((unit_info, function_dies));
             }
         }
-        Err(DebugError::Other(anyhow::anyhow!(
+        Err(DebugError::Other(format!(
             "No function DIE's at address {address:#x}."
         )))
     }
@@ -927,16 +926,15 @@ impl DebugInfo {
         for unit_info in &self.unit_infos {
             if let Some(unit_offset) = offset.to_unit_offset(&unit_info.unit.header) {
                 return unit_info.unit.entry(unit_offset).map_err(|error| {
-                    DebugError::Other(anyhow::anyhow!(
+                    DebugError::Other(format!(
                         "Error reading DIE at debug info offset {:#x} : {}",
-                        offset.0,
-                        error
+                        offset.0, error
                     ))
                 });
             }
         }
 
-        Err(DebugError::Other(anyhow::anyhow!(
+        Err(DebugError::Other(format!(
             "DIE at debug info offset {:#010x} not found",
             offset.0
         )))
@@ -989,10 +987,9 @@ pub fn get_unwind_info<'a>(
     frame_program_counter: u64,
 ) -> Result<&'a gimli::UnwindTableRow<GimliReaderOffset>, DebugError> {
     let transform_error = |error| {
-        DebugError::Other(anyhow::anyhow!(
+        DebugError::Other(format!(
             "UNWIND: Error reading FrameDescriptorEntry at PC={} : {}",
-            frame_program_counter,
-            error
+            frame_program_counter, error
         ))
     };
 
@@ -1125,20 +1122,18 @@ pub fn unwind_register(
                         .get_register_value_by_role(&RegisterRole::ProgramCounter)
                     else {
                         return ControlFlow::Break(
-                            anyhow!(
-                                "UNWIND: Tried to unwind return address value where current program counter is unknown."
+                            crate::Error::Other(
+                                "UNWIND: Tried to unwind return address value where current program counter is unknown.".to_string()
                             )
-                            .into(),
                         );
                     };
                     let Ok(current_lr) = callee_frame_registers
                         .get_register_value_by_role(&RegisterRole::ReturnAddress)
                     else {
                         return ControlFlow::Break(
-                            anyhow!(
-                                "UNWIND: Tried to unwind return address value where current return address is unknown."
+                            crate::Error::Other(
+                                "UNWIND: Tried to unwind return address value where current return address is unknown.".to_string()
                             )
-                            .into(),
                         );
                     };
                     *unwound_return_address = if current_pc == current_lr & !0b1 {
@@ -1202,9 +1197,9 @@ pub fn unwind_register(
         RegisterRule::Offset(address_offset) => {
             // "The previous value of this register is saved at the address CFA+N where CFA is the current CFA value and N is a signed offset"
             let Some(unwind_cfa) = unwind_cfa else {
-                return ControlFlow::Break(
-                    anyhow!("UNWIND: Tried to unwind `RegisterRule` at CFA = None.").into(),
-                );
+                return ControlFlow::Break(crate::Error::Other(
+                    "UNWIND: Tried to unwind `RegisterRule` at CFA = None.".to_string(),
+                ));
             };
             let address_size = callee_frame_registers.get_address_size_bytes();
             let previous_frame_register_address =
@@ -1225,9 +1220,10 @@ pub fn unwind_register(
                         .map(|_| RegisterValue::U64(u64::from_le_bytes(buff)))
                 }
                 _ => {
-                    return ControlFlow::Break(
-                        anyhow!("UNWIND: Address size {} not supported.", address_size).into(),
-                    );
+                    return ControlFlow::Break(Error::Other(format!(
+                        "UNWIND: Address size {} not supported.",
+                        address_size
+                    )));
                 }
             };
 
@@ -1250,14 +1246,13 @@ pub fn unwind_register(
                     );
 
                     return ControlFlow::Break(
-                        anyhow!(
+                        Error::Other(format!(
                             "UNWIND: Failed to read value for register {} from address {} ({} bytes): {}",
                             debug_register.get_register_name(),
                             RegisterValue::from(previous_frame_register_address),
                             4,
                             error
-                        )
-                        .into(),
+                        )),
                     );
                 }
             }
diff --git a/probe-rs/src/debug/debug_step.rs b/probe-rs/src/debug/debug_step.rs
index 571e7aee..88818fdb 100644
--- a/probe-rs/src/debug/debug_step.rs
+++ b/probe-rs/src/debug/debug_step.rs
@@ -41,17 +41,15 @@ impl SteppingMode {
         core: &mut impl CoreInterface,
         debug_info: &DebugInfo,
     ) -> Result<(CoreStatus, u64), DebugError> {
-        let mut core_status = core
-            .status()
-            .map_err(|error| DebugError::Other(anyhow::anyhow!(error)))?;
+        let mut core_status = core.status()?;
         let mut program_counter = match core_status {
             CoreStatus::Halted(_) => core
                 .read_core_reg(core.program_counter().id())?
                 .try_into()?,
             _ => {
-                return Err(DebugError::Other(anyhow::anyhow!(
-                    "Core must be halted before stepping."
-                )))
+                return Err(DebugError::Other(
+                    "Core must be halted before stepping.".to_string(),
+                ))
             }
         };
         let origin_program_counter = program_counter;
@@ -264,7 +262,7 @@ impl SteppingMode {
                             .attribute(debug_info, gimli::DW_AT_noreturn)
                             .is_some()
                         {
-                            return Err(DebugError::Other(anyhow::anyhow!(
+                            return Err(DebugError::Other(format!(
                                 "Function {:?} is marked as `noreturn`. Cannot step out of this function.",
                                 function.function_name(debug_info).as_deref().unwrap_or("<unknown>")
                             )));
@@ -361,7 +359,7 @@ fn run_to_address(
                     (core.status()?, program_counter)
                 } else {
                     // Something else is wrong.
-                    return Err(DebugError::Other(anyhow::anyhow!(
+                    return Err(DebugError::Other(format!(
                         "Unexpected error while waiting for the core to halt after stepping to {:#010X}. Forced a halt at {:#010X}. {:?}.",
                         program_counter,
                         target_address,
@@ -407,7 +405,7 @@ fn step_to_address(
             },
             // This is not a recoverable error, and will result in the debug session ending (we have no predicatable way of successfully continuing the session)
             other_status => return Err(DebugError::Other(
-                anyhow::anyhow!("Target failed to reach the destination address of a step operation: {:?}", other_status))
+                format!("Target failed to reach the destination address of a step operation: {:?}", other_status))
             ),
         }
     }
diff --git a/probe-rs/src/debug/exception_handling.rs b/probe-rs/src/debug/exception_handling.rs
index a6ee7146..4fa2bd4c 100644
--- a/probe-rs/src/debug/exception_handling.rs
+++ b/probe-rs/src/debug/exception_handling.rs
@@ -2,9 +2,9 @@
 
 use probe_rs_target::CoreType;
 
-use crate::{Error, MemoryInterface};
+use crate::MemoryInterface;
 
-use super::{DebugInfo, DebugRegisters, StackFrame};
+use super::{DebugError, DebugInfo, DebugRegisters, StackFrame};
 
 pub(crate) mod armv6m;
 /// Where applicable, this defines shared logic for implementing exception handling across the various ARMv6-m and ARMv7-m [`crate::CoreType`]'s.
@@ -36,7 +36,7 @@ impl ExceptionInterface for UnimplementedExceptionHandler {
         _memory: &mut dyn MemoryInterface,
         _stackframe_registers: &DebugRegisters,
         _debug_info: &DebugInfo,
-    ) -> Result<Option<ExceptionInfo>, Error> {
+    ) -> Result<Option<ExceptionInfo>, DebugError> {
         // For architectures where the exception handling has not been implemented in probe-rs,
         // this will result in maintaining the current `unwind` behavior, i.e. unwinding will include up
         // to the first frame that was called from an exception handler.
@@ -48,15 +48,15 @@ impl ExceptionInterface for UnimplementedExceptionHandler {
         _memory: &mut dyn MemoryInterface,
         _stackframe_registers: &crate::debug::DebugRegisters,
         _raw_exception: u32,
-    ) -> Result<crate::debug::DebugRegisters, crate::Error> {
-        Err(Error::NotImplemented("calling frame registers"))
+    ) -> Result<crate::debug::DebugRegisters, DebugError> {
+        Err(DebugError::NotImplemented("calling frame registers"))
     }
 
     fn raw_exception(
         &self,
         _stackframe_registers: &crate::debug::DebugRegisters,
-    ) -> Result<u32, crate::Error> {
-        Err(Error::NotImplemented(
+    ) -> Result<u32, DebugError> {
+        Err(DebugError::NotImplemented(
             "Not implemented for this architecture.",
         ))
     }
@@ -65,8 +65,8 @@ impl ExceptionInterface for UnimplementedExceptionHandler {
         &self,
         _raw_exception: u32,
         _memory: &mut dyn MemoryInterface,
-    ) -> Result<String, crate::Error> {
-        Err(Error::NotImplemented("exception description"))
+    ) -> Result<String, DebugError> {
+        Err(DebugError::NotImplemented("exception description"))
     }
 }
 
@@ -97,7 +97,7 @@ pub trait ExceptionInterface {
         memory: &mut dyn MemoryInterface,
         stackframe_registers: &DebugRegisters,
         debug_info: &DebugInfo,
-    ) -> Result<Option<ExceptionInfo>, Error>;
+    ) -> Result<Option<ExceptionInfo>, DebugError>;
 
     /// Using the `stackframe_registers` for a "called frame", retrieve updated register values for the "calling frame".
     fn calling_frame_registers(
@@ -105,13 +105,13 @@ pub trait ExceptionInterface {
         memory: &mut dyn MemoryInterface,
         stackframe_registers: &crate::debug::DebugRegisters,
         raw_exception: u32,
-    ) -> Result<crate::debug::DebugRegisters, crate::Error>;
+    ) -> Result<crate::debug::DebugRegisters, DebugError>;
 
     /// Retrieve the architecture specific exception number.
     fn raw_exception(
         &self,
         stackframe_registers: &crate::debug::DebugRegisters,
-    ) -> Result<u32, crate::Error>;
+    ) -> Result<u32, DebugError>;
 
     /// Convert the architecture specific exception number into a human readable description.
     /// Where possible, the implementation may read additional registers from the core, to provide additional context.
@@ -119,5 +119,5 @@ pub trait ExceptionInterface {
         &self,
         raw_exception: u32,
         memory: &mut dyn MemoryInterface,
-    ) -> Result<String, crate::Error>;
+    ) -> Result<String, DebugError>;
 }
diff --git a/probe-rs/src/debug/exception_handling/armv6m.rs b/probe-rs/src/debug/exception_handling/armv6m.rs
index e4ba0236..a0d33d0a 100644
--- a/probe-rs/src/debug/exception_handling/armv6m.rs
+++ b/probe-rs/src/debug/exception_handling/armv6m.rs
@@ -1,5 +1,5 @@
 use crate::{
-    debug::{DebugInfo, DebugRegisters},
+    debug::{DebugError, DebugInfo, DebugRegisters},
     Error, MemoryInterface,
 };
 
@@ -47,21 +47,18 @@ impl From<u32> for ExceptionReason {
 impl ExceptionReason {
     /// Expands the exception reason, by providing additional information about the exception from the
     /// HFSR and CFSR registers.
-    pub(crate) fn expanded_description(
-        &self,
-        _memory: &mut dyn MemoryInterface,
-    ) -> Result<String, Error> {
+    pub(crate) fn expanded_description(&self) -> String {
         match self {
-            ExceptionReason::ThreadMode => Ok("<No active exception>".to_string()),
-            ExceptionReason::Reset => Ok("Reset".to_string()),
-            ExceptionReason::NonMaskableInterrupt => Ok("NMI".to_string()),
-            ExceptionReason::HardFault => Ok("HardFault".to_string()),
-            ExceptionReason::SVCall => Ok("SVC".to_string()),
-            ExceptionReason::PendSV => Ok("PendSV".to_string()),
-            ExceptionReason::SysTick => Ok("SysTick".to_string()),
-            ExceptionReason::ExternalInterrupt(exti) => Ok(format!("External interrupt #{exti}")),
+            ExceptionReason::ThreadMode => "<No active exception>".to_string(),
+            ExceptionReason::Reset => "Reset".to_string(),
+            ExceptionReason::NonMaskableInterrupt => "NMI".to_string(),
+            ExceptionReason::HardFault => "HardFault".to_string(),
+            ExceptionReason::SVCall => "SVC".to_string(),
+            ExceptionReason::PendSV => "PendSV".to_string(),
+            ExceptionReason::SysTick => "SysTick".to_string(),
+            ExceptionReason::ExternalInterrupt(exti) => format!("External interrupt #{exti}"),
             ExceptionReason::Reserved => {
-                Ok("<Reserved by the ISA, and not usable by software>".to_string())
+                "<Reserved by the ISA, and not usable by software>".to_string()
             }
         }
     }
@@ -92,7 +89,7 @@ impl ExceptionInterface for ArmV6MExceptionHandler {
         memory_interface: &mut dyn MemoryInterface,
         stackframe_registers: &DebugRegisters,
         debug_info: &DebugInfo,
-    ) -> Result<Option<ExceptionInfo>, Error> {
+    ) -> Result<Option<ExceptionInfo>, DebugError> {
         armv6m_armv7m_shared::exception_details(
             self,
             memory_interface,
@@ -106,7 +103,7 @@ impl ExceptionInterface for ArmV6MExceptionHandler {
         memory_interface: &mut dyn MemoryInterface,
         stackframe_registers: &crate::debug::DebugRegisters,
         raw_exception: u32,
-    ) -> Result<crate::debug::DebugRegisters, Error> {
+    ) -> Result<crate::debug::DebugRegisters, DebugError> {
         let mut updated_registers = stackframe_registers.clone();
 
         // Identify the correct location for the exception context. This is different between Armv6-M and Armv7-M.
@@ -131,20 +128,21 @@ impl ExceptionInterface for ArmV6MExceptionHandler {
     fn raw_exception(
         &self,
         stackframe_registers: &crate::debug::DebugRegisters,
-    ) -> Result<u32, Error> {
-        armv6m_armv7m_shared::raw_exception(stackframe_registers)
+    ) -> Result<u32, DebugError> {
+        let value = armv6m_armv7m_shared::raw_exception(stackframe_registers)?;
+        Ok(value)
     }
 
     fn exception_description(
         &self,
         raw_exception: u32,
-        memory_interface: &mut dyn MemoryInterface,
-    ) -> Result<String, Error> {
+        _memory_interface: &mut dyn MemoryInterface,
+    ) -> Result<String, DebugError> {
         // TODO: Some ARMv6-M cores (e.g. the Cortex-M0) do not have HFSR and CFGR registers, so we cannot
         //       determine the cause of the hard fault. We should add a check for this, and return a more
         //       helpful error message in this case (I'm not sure this is possible).
         //       Until then, this will return a generic error message for all hard faults on this architecture.
-        ExceptionReason::from(raw_exception).expanded_description(memory_interface)
+        Ok(ExceptionReason::from(raw_exception).expanded_description())
     }
 }
 
diff --git a/probe-rs/src/debug/exception_handling/armv6m_armv7m_shared.rs b/probe-rs/src/debug/exception_handling/armv6m_armv7m_shared.rs
index 715dfe71..7d97409f 100644
--- a/probe-rs/src/debug/exception_handling/armv6m_armv7m_shared.rs
+++ b/probe-rs/src/debug/exception_handling/armv6m_armv7m_shared.rs
@@ -77,7 +77,7 @@ pub(crate) fn exception_details(
     memory_interface: &mut dyn MemoryInterface,
     stackframe_registers: &DebugRegisters,
     debug_info: &DebugInfo,
-) -> Result<Option<ExceptionInfo>, Error> {
+) -> Result<Option<ExceptionInfo>, DebugError> {
     let frame_return_address = get_stack_frame_return_address(stackframe_registers)?;
 
     if ExcReturn(frame_return_address).is_exception_flag() != 0xF {
@@ -120,8 +120,7 @@ pub(crate) fn exception_details(
             &mut unwind_context,
             &debug_info.frame_section,
             exception_frame_pc,
-        )
-        .map_err(|error: DebugError| Error::Other(error.into()))?;
+        )?;
         handler_frame.canonical_frame_address =
             determine_cfa(&handler_frame.registers, unwind_info)?;
         let Ok((_, functions)) = debug_info.get_function_dies(exception_frame_pc) else {
@@ -142,17 +141,15 @@ pub(crate) fn exception_details(
                 handler_frame,
             }));
         }
-        handler_frame.frame_base = functions[0]
-            .frame_base(
-                debug_info,
-                memory_interface,
-                StackFrameInfo {
-                    registers: &handler_frame.registers,
-                    frame_base: None,
-                    canonical_frame_address: handler_frame.canonical_frame_address,
-                },
-            )
-            .map_err(|error: DebugError| Error::Other(error.into()))?;
+        handler_frame.frame_base = functions[0].frame_base(
+            debug_info,
+            memory_interface,
+            StackFrameInfo {
+                registers: &handler_frame.registers,
+                frame_base: None,
+                canonical_frame_address: handler_frame.canonical_frame_address,
+            },
+        )?;
         let callee_frame_registers = handler_frame.registers.clone();
         if let ControlFlow::Break(error) = unwind_register(
             handler_frame
diff --git a/probe-rs/src/debug/exception_handling/armv7m.rs b/probe-rs/src/debug/exception_handling/armv7m.rs
index 4105994c..86e49766 100644
--- a/probe-rs/src/debug/exception_handling/armv7m.rs
+++ b/probe-rs/src/debug/exception_handling/armv7m.rs
@@ -1,6 +1,6 @@
 use super::{armv6m_armv7m_shared, ExceptionInfo, ExceptionInterface};
 use crate::{
-    debug::{DebugInfo, DebugRegisters},
+    debug::{DebugError, DebugInfo, DebugRegisters},
     memory_mapped_bitfield_register, Error, MemoryInterface, MemoryMappedRegister,
 };
 
@@ -321,7 +321,7 @@ impl ExceptionInterface for ArmV7MExceptionHandler {
         memory_interface: &mut dyn MemoryInterface,
         stackframe_registers: &DebugRegisters,
         debug_info: &DebugInfo,
-    ) -> Result<Option<ExceptionInfo>, Error> {
+    ) -> Result<Option<ExceptionInfo>, DebugError> {
         armv6m_armv7m_shared::exception_details(
             self,
             memory_interface,
@@ -335,7 +335,7 @@ impl ExceptionInterface for ArmV7MExceptionHandler {
         memory_interface: &mut dyn MemoryInterface,
         stackframe_registers: &crate::debug::DebugRegisters,
         raw_exception: u32,
-    ) -> Result<crate::debug::DebugRegisters, crate::Error> {
+    ) -> Result<crate::debug::DebugRegisters, DebugError> {
         let mut updated_registers = stackframe_registers.clone();
 
         // Identify the correct location for the exception context. This is different between Armv6-M and Armv7-M.
@@ -359,15 +359,18 @@ impl ExceptionInterface for ArmV7MExceptionHandler {
     fn raw_exception(
         &self,
         stackframe_registers: &crate::debug::DebugRegisters,
-    ) -> Result<u32, Error> {
-        armv6m_armv7m_shared::raw_exception(stackframe_registers)
+    ) -> Result<u32, DebugError> {
+        let value = armv6m_armv7m_shared::raw_exception(stackframe_registers)?;
+        Ok(value)
     }
 
     fn exception_description(
         &self,
         raw_exception: u32,
         memory_interface: &mut dyn MemoryInterface,
-    ) -> Result<String, crate::Error> {
-        ExceptionReason::from(raw_exception).expanded_description(memory_interface)
+    ) -> Result<String, DebugError> {
+        let description =
+            ExceptionReason::from(raw_exception).expanded_description(memory_interface)?;
+        Ok(description)
     }
 }
diff --git a/probe-rs/src/debug/exception_handling/armv8m.rs b/probe-rs/src/debug/exception_handling/armv8m.rs
index e0710760..bf1eee72 100644
--- a/probe-rs/src/debug/exception_handling/armv8m.rs
+++ b/probe-rs/src/debug/exception_handling/armv8m.rs
@@ -1,5 +1,5 @@
 use crate::{
-    debug::{get_object_reference, DebugInfo, DebugRegisters, StackFrame},
+    debug::{get_object_reference, DebugError, DebugInfo, DebugRegisters, StackFrame},
     memory::MemoryInterface,
     memory_mapped_bitfield_register, Error, MemoryMappedRegister, RegisterRole, RegisterValue,
 };
@@ -195,6 +195,9 @@ memory_mapped_bitfield_register! {
 
 impl Sfsr {
     /// Additional information about a Secure Fault, or Ok(None) if the fault was not a Secure Fault.
+    ///
+    /// This function will access the memory interface to determine the address of the fault,
+    /// if necessary.
     fn secure_fault_description(
         &self,
         memory: &mut dyn MemoryInterface,
@@ -307,7 +310,7 @@ impl From<u32> for ExceptionReason {
 impl ExceptionReason {
     /// Expands the exception reason, by providing additional information about the exception from the
     /// HFSR, CFSR, and SFSR registers.
-    fn expanded_description(&self, memory: &mut dyn MemoryInterface) -> Result<String, Error> {
+    fn expanded_description(&self, memory: &mut dyn MemoryInterface) -> Result<String, DebugError> {
         match self {
             ExceptionReason::ThreadMode => Ok("<No active exception>".to_string()),
             ExceptionReason::Reset => Ok("Reset".to_string()),
@@ -390,7 +393,7 @@ impl ExceptionInterface for ArmV8MExceptionHandler {
         memory_interface: &mut dyn MemoryInterface,
         stackframe_registers: &DebugRegisters,
         _raw_exception: u32,
-    ) -> Result<DebugRegisters, Error> {
+    ) -> Result<DebugRegisters, DebugError> {
         let mut calling_stack_registers = vec![0u32; EXCEPTION_STACK_REGISTERS.len()];
         let stack_frame_return_address: u32 = get_stack_frame_return_address(stackframe_registers)?;
         let exc_return = ExcReturn(stack_frame_return_address);
@@ -437,7 +440,7 @@ impl ExceptionInterface for ArmV8MExceptionHandler {
         Ok(calling_frame_registers)
     }
 
-    fn raw_exception(&self, stackframe_registers: &DebugRegisters) -> Result<u32, Error> {
+    fn raw_exception(&self, stackframe_registers: &DebugRegisters) -> Result<u32, DebugError> {
         // Load the provided xPSR register as a bitfield.
         let exception_number = Xpsr(
             stackframe_registers
@@ -453,7 +456,7 @@ impl ExceptionInterface for ArmV8MExceptionHandler {
         &self,
         raw_exception: u32,
         memory_interface: &mut dyn MemoryInterface,
-    ) -> Result<String, Error> {
+    ) -> Result<String, DebugError> {
         ExceptionReason::from(raw_exception).expanded_description(memory_interface)
     }
 
@@ -462,7 +465,7 @@ impl ExceptionInterface for ArmV8MExceptionHandler {
         memory_interface: &mut dyn MemoryInterface,
         stackframe_registers: &DebugRegisters,
         _debug_info: &DebugInfo,
-    ) -> Result<Option<ExceptionInfo>, Error> {
+    ) -> Result<Option<ExceptionInfo>, DebugError> {
         let stack_frame_return_address: u32 = get_stack_frame_return_address(stackframe_registers)?;
         if ExcReturn(stack_frame_return_address).is_exception_flag() == 0xFF {
             // This is an exception frame.
diff --git a/probe-rs/src/debug/language.rs b/probe-rs/src/debug/language.rs
index 229b0351..a595d128 100644
--- a/probe-rs/src/debug/language.rs
+++ b/probe-rs/src/debug/language.rs
@@ -99,7 +99,7 @@ impl ProgrammingLanguage for UnknownLanguage {
         _memory: &mut dyn MemoryInterface,
         _new_value: &str,
     ) -> Result<(), DebugError> {
-        Err(DebugError::Other(anyhow::anyhow!(
+        Err(DebugError::Other(format!(
             "Updating variables for language {} is not supported.",
             self.0
         )))
diff --git a/probe-rs/src/debug/mod.rs b/probe-rs/src/debug/mod.rs
index dfe4ae80..ed7ff708 100644
--- a/probe-rs/src/debug/mod.rs
+++ b/probe-rs/src/debug/mod.rs
@@ -39,6 +39,7 @@ use gimli::EvaluationResult;
 use serde::Serialize;
 use typed_path::TypedPathBuf;
 
+use std::num::ParseIntError;
 use std::{
     io,
     num::NonZeroU32,
@@ -82,9 +83,14 @@ pub enum DebugError {
         /// A message that can be displayed to the user to help them understand the reason for the incomplete results.
         message: String,
     },
+
+    /// Required functionality is not implemented
+    #[error("Not implemented: {0}")]
+    NotImplemented(&'static str),
+
     /// Some other error occurred.
-    #[error(transparent)]
-    Other(#[from] anyhow::Error),
+    #[error("{0}")]
+    Other(String),
 }
 
 /// A copy of [`gimli::ColumnType`] which uses [`u64`] instead of [`NonZeroU64`](std::num::NonZeroU64).
@@ -156,7 +162,7 @@ impl From<i64> for ObjectRef {
 }
 
 impl std::str::FromStr for ObjectRef {
-    type Err = anyhow::Error;
+    type Err = ParseIntError;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let value = s.parse::<i64>()?;
diff --git a/probe-rs/src/debug/source_instructions.rs b/probe-rs/src/debug/source_instructions.rs
index 3e1d4a8f..8ab6b402 100644
--- a/probe-rs/src/debug/source_instructions.rs
+++ b/probe-rs/src/debug/source_instructions.rs
@@ -158,7 +158,7 @@ impl VerifiedBreakpoint {
             }
         }
         // If we get here, we have not found a valid breakpoint location.
-        Err(DebugError::Other(anyhow::anyhow!("No valid breakpoint information found for file: {}, line: {line:?}, column: {column:?}", path.to_path().display())))
+        Err(DebugError::Other(format!("No valid breakpoint information found for file: {}, line: {line:?}, column: {column:?}", path.to_path().display())))
     }
 }
 
diff --git a/probe-rs/src/debug/unit_info.rs b/probe-rs/src/debug/unit_info.rs
index c9c8dd1a..c413b6e9 100644
--- a/probe-rs/src/debug/unit_info.rs
+++ b/probe-rs/src/debug/unit_info.rs
@@ -54,9 +54,9 @@ impl UnitInfo {
     }
 
     pub(crate) fn debug_info_offset(&self) -> Result<DebugInfoOffset, DebugError> {
-        self.unit.header.offset().as_debug_info_offset().ok_or_else(|| DebugError::Other(anyhow::anyhow!(
-            "Failed to convert unit header offset to debug info offset. This is a bug, please report it."
-        )))
+        self.unit.header.offset().as_debug_info_offset().ok_or_else(|| DebugError::Other(
+            "Failed to convert unit header offset to debug info offset. This is a bug, please report it.".to_string()
+        ))
     }
 
     /// Get the compilation unit DIEs for the function containing the given address.
@@ -822,7 +822,7 @@ impl UnitInfo {
                 gimli::DW_AT_lower_bound => match attr.value().udata_value() {
                     Some(bound) => lower_bound = Some(bound),
                     None => {
-                        return Err(DebugError::Other(anyhow::anyhow!(
+                        return Err(DebugError::Other(format!(
                             "Unimplemented: Attribute Value for DW_AT_lower_bound: {:?}",
                             attr.value()
                         )));
@@ -831,7 +831,7 @@ impl UnitInfo {
                 gimli::DW_AT_count => match attr.value().udata_value() {
                     Some(count) => upper_bound = Some(count),
                     None => {
-                        return Err(DebugError::Other(anyhow::anyhow!(
+                        return Err(DebugError::Other(format!(
                             "Unimplemented: Attribute Value for DW_AT_count: {:?}",
                             attr.value()
                         )));
@@ -842,7 +842,7 @@ impl UnitInfo {
                         // Rust ranges are exclusive, but the DWARF upper bound is inclusive.
                         Some(bound) => upper_bound = Some(bound + 1),
                         None => {
-                            return Err(DebugError::Other(anyhow::anyhow!(
+                            return Err(DebugError::Other(format!(
                                 "Unimplemented: Attribute Value for DW_AT_upper_bound: {:?}",
                                 attr.value()
                             )));
diff --git a/probe-rs/src/debug/variable.rs b/probe-rs/src/debug/variable.rs
index 6268e9c8..7bef0815 100644
--- a/probe-rs/src/debug/variable.rs
+++ b/probe-rs/src/debug/variable.rs
@@ -1,7 +1,6 @@
 use crate::debug::{language::ProgrammingLanguage, unit_info::UnitInfo};
 
 use super::*;
-use anyhow::anyhow;
 use gimli::{DebugInfoOffset, DwLang, UnitOffset};
 use itertools::Itertools;
 use std::ops::Range;
@@ -558,10 +557,10 @@ impl Variable {
         let valid_memory = self.memory_location.valid();
         if !valid_value || !valid_type || !valid_memory {
             // Insufficient data available.
-            Err(anyhow!(
+            Err(DebugError::Other(format!(
                 "Cannot update variable: {:?}, with supplied information (value={:?}, type={:?}, memory location={:#010x?}).",
                 self.name, self.value, self.type_name, self.memory_location
-            ).into())
+            )))
         } else {
             // We have everything we need to update the variable value.
             language::from_dwarf(self.language)
diff --git a/probe-rs/src/debug/variable_cache.rs b/probe-rs/src/debug/variable_cache.rs
index c38b225a..77bec8f2 100644
--- a/probe-rs/src/debug/variable_cache.rs
+++ b/probe-rs/src/debug/variable_cache.rs
@@ -3,7 +3,6 @@ use crate::{
     debug::{stack_frame::StackFrameInfo, unit_info::UnitInfo},
     Error,
 };
-use anyhow::anyhow;
 use gimli::UnitOffset;
 use probe_rs_target::MemoryRange;
 use serde::{Serialize, Serializer};
@@ -170,10 +169,12 @@ impl VariableCache {
         &mut self,
         parent_key: ObjectRef,
         unit_info: Option<&UnitInfo>,
-    ) -> Result<Variable, Error> {
+    ) -> Result<Variable, DebugError> {
         // Validate that the parent_key exists ...
         if !self.variable_hash_map.contains_key(&parent_key) {
-            return Err(anyhow!("VariableCache: Attempted to add a new variable with non existent `parent_key`: {:?}. Please report this as a bug", parent_key).into());
+            return Err(DebugError::Other(
+                format!("VariableCache: Attempted to add a new variable with non existent `parent_key`: {:?}. Please report this as a bug", parent_key)
+            ));
         }
 
         let mut variable_to_add = Variable::new(unit_info);
@@ -191,7 +192,7 @@ impl VariableCache {
 
         match self.variable_hash_map.entry(variable_to_add.variable_key) {
             Entry::Occupied(_) => {
-                return Err(anyhow!("Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.", variable_to_add.name, variable_to_add.variable_key).into());
+                return Err(DebugError::Other(format!("Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.", variable_to_add.name, variable_to_add.variable_key)));
             }
             Entry::Vacant(entry) => {
                 entry.insert(variable_to_add.clone());
@@ -209,16 +210,16 @@ impl VariableCache {
         &mut self,
         parent_key: ObjectRef,
         cache_variable: &mut Variable,
-    ) -> Result<(), Error> {
+    ) -> Result<(), DebugError> {
         // Validate that the parent_key exists ...
         if !self.variable_hash_map.contains_key(&parent_key) {
-            return Err(anyhow!("VariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug", cache_variable.name, parent_key).into());
+            return Err(DebugError::Other(format!("VariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {:?}. Please report this as a bug", cache_variable.name, parent_key)));
         }
 
         cache_variable.parent_key = parent_key;
 
         if cache_variable.variable_key != ObjectRef::Invalid {
-            return Err(anyhow!("VariableCache: Attempted to add a new variable: {} with already set key: {:?}. Please report this as a bug", cache_variable.name, cache_variable.variable_key).into());
+            return Err(DebugError::Other(format!("VariableCache: Attempted to add a new variable: {} with already set key: {:?}. Please report this as a bug", cache_variable.name, cache_variable.variable_key)));
         }
 
         // The caller is telling us this is definitely a new `Variable`
@@ -235,7 +236,7 @@ impl VariableCache {
             .variable_hash_map
             .insert(cache_variable.variable_key, cache_variable.clone())
         {
-            return Err(anyhow!("Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.", cache_variable.name, old_variable.variable_key).into());
+            return Err(DebugError::Other(format!("Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {:?}. Please report this as a bug.", cache_variable.name, old_variable.variable_key)));
         }
 
         Ok(())
@@ -244,7 +245,7 @@ impl VariableCache {
     /// Update a variable in the cache
     ///
     /// This function does not update the value of the variable.
-    pub fn update_variable(&mut self, cache_variable: &Variable) -> Result<(), Error> {
+    pub fn update_variable(&mut self, cache_variable: &Variable) -> Result<(), DebugError> {
         // Attempt to update an existing `Variable` in the cache
         tracing::trace!(
             "VariableCache: Update Variable, key={:?}, name={:?}",
@@ -253,7 +254,7 @@ impl VariableCache {
         );
 
         let Some(prev_entry) = self.variable_hash_map.get_mut(&cache_variable.variable_key) else {
-            return Err(anyhow!("Attempt to update an existing `Variable`:{:?} with a non-existent cache key: {:?}. Please report this as a bug.", cache_variable.name, cache_variable.variable_key).into());
+            return Err(DebugError::Other(format!("Attempt to update an existing `Variable`:{:?} with a non-existent cache key: {:?}. Please report this as a bug.", cache_variable.name, cache_variable.variable_key)));
         };
 
         if cache_variable != prev_entry {
@@ -387,7 +388,7 @@ impl VariableCache {
     pub fn remove_cache_entry(&mut self, variable_key: ObjectRef) -> Result<(), Error> {
         self.remove_cache_entry_children(variable_key)?;
         if self.variable_hash_map.remove(&variable_key).is_none() {
-            return Err(anyhow!("Failed to remove a `VariableCache` entry with key: {:?}. Please report this as a bug.", variable_key).into());
+            return Err(Error::Other(format!("Failed to remove a `VariableCache` entry with key: {:?}. Please report this as a bug.", variable_key)));
         };
         Ok(())
     }
diff --git a/probe-rs/src/error.rs b/probe-rs/src/error.rs
index 0c341180..883daf56 100644
--- a/probe-rs/src/error.rs
+++ b/probe-rs/src/error.rs
@@ -2,6 +2,7 @@ use crate::architecture::arm::ArmError;
 use crate::architecture::riscv::communication_interface::RiscvError;
 use crate::architecture::xtensa::communication_interface::XtensaError;
 use crate::config::RegistryError;
+use crate::core::memory_mapped_registers::RegisterAddressOutOfBounds;
 use crate::probe::DebugProbeError;
 
 /// The overarching error type which contains all possible errors as variants.
@@ -29,6 +30,10 @@ pub enum Error {
     GenericCoreError(String),
     /// Errors accessing core register: {0}
     Register(String),
+
+    /// Error calculating the address of a register
+    #[error(transparent)]
+    RegisterAddressOutOfBounds(#[from] RegisterAddressOutOfBounds),
     /// The {0} capability has not yet been implemented for this architecture.
     ///
     /// Because of the large varieties of supported architectures, it is not always possible for
@@ -38,7 +43,7 @@ pub enum Error {
     NotImplemented(&'static str),
     /// Some uncategorized error occurred.
     #[display("{0}")]
-    Other(#[from] anyhow::Error),
+    Other(String),
     /// A timeout occurred.
     // TODO: Errors below should be core specific
     Timeout,
diff --git a/probe-rs/src/gdb_server/stub.rs b/probe-rs/src/gdb_server/stub.rs
index dd422c80..ed56dd4d 100644
--- a/probe-rs/src/gdb_server/stub.rs
+++ b/probe-rs/src/gdb_server/stub.rs
@@ -1,5 +1,4 @@
 use crate::{CoreType, Session};
-use anyhow::Result;
 use parking_lot::FairMutex;
 
 use std::net::{SocketAddr, ToSocketAddrs};
@@ -84,7 +83,7 @@ impl GdbInstanceConfiguration {
 pub fn run<'a>(
     session: &FairMutex<Session>,
     instances: impl Iterator<Item = &'a GdbInstanceConfiguration>,
-) -> Result<()> {
+) -> anyhow::Result<()> {
     // Turn our group list into GDB targets
     let mut targets = instances
         .map(|instance| {
diff --git a/probe-rs/src/gdb_server/target/desc/mod.rs b/probe-rs/src/gdb_server/target/desc/mod.rs
index 984e935b..93de130f 100644
--- a/probe-rs/src/gdb_server/target/desc/mod.rs
+++ b/probe-rs/src/gdb_server/target/desc/mod.rs
@@ -26,9 +26,10 @@ impl TargetDescriptionXmlOverride for RuntimeTarget<'_> {
     ) -> gdbstub::target::TargetResult<usize, Self> {
         let annex = String::from_utf8_lossy(annex);
         if annex != "target.xml" {
-            return Err(TargetError::Fatal(
-                anyhow!("Unsupported annex: '{}'", annex).into(),
-            ));
+            return Err(TargetError::Fatal(anyhow!(
+                "Unsupported annex: '{}'",
+                annex
+            )));
         }
 
         let xml = self.target_desc.get_target_xml();
diff --git a/probe-rs/src/gdb_server/target/mod.rs b/probe-rs/src/gdb_server/target/mod.rs
index f5fd913d..1b8cee9b 100644
--- a/probe-rs/src/gdb_server/target/mod.rs
+++ b/probe-rs/src/gdb_server/target/mod.rs
@@ -26,7 +26,7 @@ use gdbstub::target::ext::monitor_cmd::MonitorCmdOps;
 use gdbstub::target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps;
 use gdbstub::target::Target;
 
-pub(crate) use traits::{GdbErrorExt, ProbeRsErrorExt};
+pub(crate) use traits::GdbErrorExt;
 
 use desc::TargetDescription;
 
@@ -65,9 +65,9 @@ impl<'a> RuntimeTarget<'a> {
         session: &'a FairMutex<Session>,
         cores: Vec<usize>,
         addrs: &[SocketAddr],
-    ) -> Result<Self, Error> {
-        let listener = TcpListener::bind(addrs).into_error()?;
-        listener.set_nonblocking(true).into_error()?;
+    ) -> Result<Self, anyhow::Error> {
+        let listener = TcpListener::bind(addrs)?;
+        listener.set_nonblocking(true)?;
 
         Ok(Self {
             session,
@@ -82,7 +82,7 @@ impl<'a> RuntimeTarget<'a> {
     /// Process any pending work for this target
     ///
     /// Returns: Duration to wait before processing this target again
-    pub fn process(&mut self) -> Result<Duration, Error> {
+    pub fn process(&mut self) -> Result<Duration, anyhow::Error> {
         // State 1 - unconnected
         if self.gdb.is_none() {
             // See if we have a connection
@@ -96,7 +96,7 @@ impl<'a> RuntimeTarget<'a> {
                     return Ok(Duration::from_millis(10));
                 }
                 // Fatal error
-                Err(e) => return Err(anyhow::anyhow!(e).into()),
+                Err(e) => return Err(e.into()),
             };
 
             // When we first attach to the core, GDB expects us to halt the core,
@@ -151,7 +151,7 @@ impl<'a> RuntimeTarget<'a> {
         &mut self,
         mut state: GdbStubStateMachineInner<'b, state::Idle<Self>, Self, TcpStream>,
         wait_time: &mut Duration,
-    ) -> Result<Option<GdbStubStateMachine<'b, Self, TcpStream>>, Error> {
+    ) -> Result<Option<GdbStubStateMachine<'b, Self, TcpStream>>, anyhow::Error> {
         let next_byte = {
             let conn = state.borrow_conn();
 
@@ -159,7 +159,7 @@ impl<'a> RuntimeTarget<'a> {
         };
 
         let next_state = if let Some(b) = next_byte {
-            state.incoming_data(self, b).into_error()?
+            state.incoming_data(self, b)?
         } else {
             *wait_time = Duration::from_millis(10);
             state.into()
@@ -172,7 +172,7 @@ impl<'a> RuntimeTarget<'a> {
         &mut self,
         mut state: GdbStubStateMachineInner<'b, state::Running, Self, TcpStream>,
         wait_time: &mut Duration,
-    ) -> Result<Option<GdbStubStateMachine<'b, Self, TcpStream>>, Error> {
+    ) -> Result<Option<GdbStubStateMachine<'b, Self, TcpStream>>, anyhow::Error> {
         let next_byte = {
             let conn = state.borrow_conn();
 
@@ -180,7 +180,7 @@ impl<'a> RuntimeTarget<'a> {
         };
 
         if let Some(b) = next_byte {
-            return Ok(Some(state.incoming_data(self, b).into_error()?));
+            return Ok(Some(state.incoming_data(self, b)?));
         }
 
         // Check for break
@@ -217,7 +217,7 @@ impl<'a> RuntimeTarget<'a> {
             // Halt all remaining cores that are still running.
             // GDB expects all or nothing stops.
             self.halt_all_cores()?;
-            state.report_stop(self, reason).into_error()?
+            state.report_stop(self, reason)?
         } else {
             *wait_time = Duration::from_millis(10);
             state.into()
@@ -229,11 +229,10 @@ impl<'a> RuntimeTarget<'a> {
     fn handle_ctrl_c<'b>(
         &mut self,
         state: GdbStubStateMachineInner<'b, state::CtrlCInterrupt, Self, TcpStream>,
-    ) -> Result<Option<GdbStubStateMachine<'b, Self, TcpStream>>, Error> {
+    ) -> Result<Option<GdbStubStateMachine<'b, Self, TcpStream>>, anyhow::Error> {
         self.halt_all_cores()?;
-        let next_state = state
-            .interrupt_handled(self, Some(MultiThreadStopReason::Signal(Signal::SIGINT)))
-            .into_error()?;
+        let next_state =
+            state.interrupt_handled(self, Some(MultiThreadStopReason::Signal(Signal::SIGINT)))?;
 
         Ok(Some(next_state))
     }
@@ -241,7 +240,7 @@ impl<'a> RuntimeTarget<'a> {
 
 impl Target for RuntimeTarget<'_> {
     type Arch = RuntimeArch;
-    type Error = Error;
+    type Error = anyhow::Error;
 
     fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> {
         BaseOps::MultiThread(self)
@@ -271,16 +270,16 @@ impl Target for RuntimeTarget<'_> {
 }
 
 /// Read a byte from a stream if available, otherwise return None
-fn read_if_available(conn: &mut TcpStream) -> Result<Option<u8>, Error> {
+fn read_if_available(conn: &mut TcpStream) -> Result<Option<u8>, anyhow::Error> {
     match conn.peek() {
         Ok(p) => {
             // Unwrap is safe because peek already showed
             // there's data in the buffer
             match p {
-                Some(_) => conn.read().map(Some).into_error(),
+                Some(_) => conn.read().map(Some).map_err(|e| e.into()),
                 None => Ok(None),
             }
         }
-        Err(e) => Err(anyhow::Error::from(e).into()),
+        Err(e) => Err(anyhow::Error::from(e)),
     }
 }
diff --git a/probe-rs/src/gdb_server/target/traits.rs b/probe-rs/src/gdb_server/target/traits.rs
index 919f2c09..33fd531d 100644
--- a/probe-rs/src/gdb_server/target/traits.rs
+++ b/probe-rs/src/gdb_server/target/traits.rs
@@ -1,29 +1,8 @@
 use super::RuntimeTarget;
 use crate::Error;
 
-use gdbstub::stub::GdbStubError;
 use gdbstub::target::{TargetError, TargetResult};
 
-pub(crate) trait ProbeRsErrorExt<T> {
-    fn into_error(self) -> Result<T, Error>;
-}
-
-impl<T> ProbeRsErrorExt<T> for Result<T, std::io::Error> {
-    fn into_error(self) -> Result<T, Error> {
-        self.map_err(|e| Error::Other(e.into()))
-    }
-}
-
-impl<T> ProbeRsErrorExt<T> for Result<T, GdbStubError<Error, std::io::Error>> {
-    fn into_error(self) -> Result<T, Error> {
-        match self {
-            Ok(v) => Ok(v),
-            Err(e) if e.is_target_error() => Err(e.into_target_error().unwrap()),
-            Err(other) => Err(anyhow::Error::new(other).into()),
-        }
-    }
-}
-
 pub(crate) trait GdbErrorExt<T> {
     fn into_target_result(self) -> TargetResult<T, RuntimeTarget<'static>>;
 
@@ -34,7 +13,7 @@ impl<T> GdbErrorExt<T> for Result<T, Error> {
     fn into_target_result(self) -> TargetResult<T, RuntimeTarget<'static>> {
         match self {
             Ok(v) => Ok(v),
-            Err(e) => Err(TargetError::Fatal(e)),
+            Err(e) => Err(TargetError::Fatal(e.into())),
         }
     }
 
@@ -56,7 +35,7 @@ impl<T> GdbErrorExt<T> for Result<T, Error> {
                 // EIO
                 Err(TargetError::Errno(122))
             }
-            Err(e) => Err(TargetError::Fatal(e)),
+            Err(e) => Err(TargetError::Fatal(e.into())),
         }
     }
 }
diff --git a/probe-rs/src/memory/mod.rs b/probe-rs/src/memory/mod.rs
index b60242fd..c5551e34 100644
--- a/probe-rs/src/memory/mod.rs
+++ b/probe-rs/src/memory/mod.rs
@@ -1,6 +1,5 @@
 use crate::error::Error;
 
-use anyhow::{anyhow, Result};
 use scroll::Pread;
 
 /// An interface to be implemented for drivers that allow target memory access.
@@ -67,9 +66,9 @@ pub trait MemoryInterface {
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 8 != 0 {
-            return Err(Error::Other(anyhow!(
-                "Call to read_mem_64bit with data.len() not a multiple of 8"
-            )));
+            return Err(Error::Other(
+                "Call to read_mem_64bit with data.len() not a multiple of 8".to_string(),
+            ));
         }
         let mut buffer = vec![0u64; data.len() / 8];
         self.read_64(address, &mut buffer)?;
@@ -89,9 +88,9 @@ pub trait MemoryInterface {
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 4 != 0 {
-            return Err(Error::Other(anyhow!(
-                "Call to read_mem_32bit with data.len() not a multiple of 4"
-            )));
+            return Err(Error::Other(
+                "Call to read_mem_32bit with data.len() not a multiple of 4".to_string(),
+            ));
         }
         let mut buffer = vec![0u32; data.len() / 4];
         self.read_32(address, &mut buffer)?;
@@ -180,9 +179,9 @@ pub trait MemoryInterface {
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 8 != 0 {
-            return Err(Error::Other(anyhow!(
-                "Call to read_mem_64bit with data.len() not a multiple of 8"
-            )));
+            return Err(Error::Other(
+                "Call to read_mem_64bit with data.len() not a multiple of 8".to_string(),
+            ));
         }
         let mut buffer = vec![0u64; data.len() / 8];
         for (bytes, value) in data.chunks_exact(8).zip(buffer.iter_mut()) {
@@ -203,9 +202,9 @@ pub trait MemoryInterface {
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 4 != 0 {
-            return Err(Error::Other(anyhow!(
-                "Call to read_mem_32bit with data.len() not a multiple of 4"
-            )));
+            return Err(Error::Other(
+                "Call to read_mem_32bit with data.len() not a multiple of 4".to_string(),
+            ));
         }
         let mut buffer = vec![0u32; data.len() / 4];
         for (bytes, value) in data.chunks_exact(4).zip(buffer.iter_mut()) {
@@ -370,7 +369,7 @@ where
 pub(crate) fn valid_32bit_address(address: u64) -> Result<u32, Error> {
     let address: u32 = address
         .try_into()
-        .map_err(|_| anyhow!("Address {:#08x} out of range", address))?;
+        .map_err(|_| Error::Other(format!("Address {:#08x} out of range", address)))?;
 
     Ok(address)
 }
diff --git a/probe-rs/src/probe.rs b/probe-rs/src/probe.rs
index ab41d5cc..c7cfeb89 100644
--- a/probe-rs/src/probe.rs
+++ b/probe-rs/src/probe.rs
@@ -26,6 +26,7 @@ use crate::config::RegistryError;
 use crate::config::TargetSelector;
 use crate::probe::common::IdCode;
 use crate::{Error, Permissions, Session};
+use common::ScanChainError;
 use nusb::DeviceInfo;
 use probe_rs_target::ScanChainElement;
 use serde::{Deserialize, Serialize};
@@ -39,7 +40,7 @@ const LOW_TARGET_VOLTAGE_WARNING_THRESHOLD: f32 = 1.4;
 
 /// The protocol that is to be used by the probe when communicating with the target.
 ///
-/// For ARM select `Swd` and for RISC-V select `Jtag`.
+/// For ARM select `Swd` or `Jtag`, for RISC-V select `Jtag`.
 #[derive(Copy, Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
 pub enum WireProtocol {
     /// Serial Wire Debug is ARMs proprietary standard for communicating with ARM cores.
@@ -177,9 +178,12 @@ pub enum DebugProbeError {
         command_name: &'static str,
     },
 
-    /// Some other error occurred.
+    /// An error occured handling the JTAG scan chain.
+    JtagScanChain(#[from] ScanChainError),
+
+    /// Some other error occured
     #[display("{0}")]
-    Other(#[from] anyhow::Error),
+    Other(String),
 
     /// A timeout occurred during probe operation.
     Timeout,
@@ -312,13 +316,14 @@ impl Probe {
             |e| match e {
                 Error::Arm(ArmError::Timeout)
                 | Error::Riscv(RiscvError::Timeout)
-                | Error::Xtensa(XtensaError::Timeout) => Error::Other(anyhow::anyhow!(
+                | Error::Xtensa(XtensaError::Timeout) => Error::Other(
                     "Timeout while attaching to target under reset. \
                     This can happen if the target is not responding to the reset sequence. \
                     Ensure the chip's reset pin is connected, or try attaching without reset \
                     (`connectUnderReset = false` for DAP Clients, or remove `connect-under-reset` \
                         option from CLI options.)."
-                )),
+                        .to_string(),
+                ),
                 e => e,
             },
         )
@@ -703,9 +708,9 @@ pub trait DebugProbe: Send + fmt::Debug {
 
     /// Try to get a J-Link interface from the debug probe.
     fn try_into_jlink(&mut self) -> Result<&mut jlink::JLink, DebugProbeError> {
-        Err(DebugProbeError::Other(anyhow::anyhow!(
-            "This probe does not support J-Link functionality."
-        )))
+        Err(DebugProbeError::Other(
+            "This probe is not a J-Link.".to_string(),
+        ))
     }
 }
 
diff --git a/probe-rs/src/probe/arm_debug_interface.rs b/probe-rs/src/probe/arm_debug_interface.rs
index 665fa609..9664f591 100644
--- a/probe-rs/src/probe/arm_debug_interface.rs
+++ b/probe-rs/src/probe/arm_debug_interface.rs
@@ -998,7 +998,7 @@ impl<Probe: DebugProbe + RawProtocolIo + JTAGAccess + 'static> RawDapAccess for
                         "Error in access {}/{} of block access: {:?}",
                         i + 1,
                         values.len(),
-                        anyhow::anyhow!(err)
+                        err
                     );
                     return Err(err.into());
                 }
diff --git a/probe-rs/src/probe/common.rs b/probe-rs/src/probe/common.rs
index 6827ec8c..46ac15d2 100644
--- a/probe-rs/src/probe/common.rs
+++ b/probe-rs/src/probe/common.rs
@@ -2,7 +2,6 @@
 
 use std::iter;
 
-use anyhow::anyhow;
 use bitfield::bitfield;
 use bitvec::prelude::*;
 use probe_rs_target::ScanChainElement;
@@ -496,7 +495,7 @@ fn shift_ir(
 
     // Check the bit length, enough data has to be available
     if data.len() * 8 < len || len == 0 {
-        return Err(DebugProbeError::Other(anyhow!(
+        return Err(DebugProbeError::Other(format!(
             "Invalid data length. IR bits: {}, expected: {}",
             data.len(),
             len
@@ -550,7 +549,7 @@ fn shift_dr(
 
     // Check the bit length, enough data has to be available
     if data.len() * 8 < register_bits || register_bits == 0 {
-        return Err(DebugProbeError::Other(anyhow!(
+        return Err(DebugProbeError::Other(format!(
             "Invalid data length. DR bits: {}, expected: {}",
             data.len(),
             register_bits
@@ -614,7 +613,7 @@ fn prepare_write_register(
 ) -> Result<usize, DebugProbeError> {
     if protocol.state().current_ir_reg != address {
         if address > protocol.state().max_ir_address {
-            return Err(DebugProbeError::Other(anyhow!(
+            return Err(DebugProbeError::Other(format!(
                 "Invalid instruction register access: {}",
                 address
             )));
@@ -644,7 +643,7 @@ impl<Probe: DebugProbe + RawJtagIo + 'static> JTAGAccess for Probe {
 
         tracing::debug!("DR: {:?}", response);
 
-        let idcodes = extract_idcodes(&response).map_err(|e| DebugProbeError::Other(e.into()))?;
+        let idcodes = extract_idcodes(&response)?;
 
         tracing::info!(
             "JTAG DR scan complete, found {} TAPs. {:?}",
@@ -692,8 +691,7 @@ impl<Probe: DebugProbe + RawJtagIo + 'static> JTAGAccess for Probe {
                         .collect::<Vec<usize>>()
                 })
                 .as_deref(),
-        )
-        .map_err(|e| DebugProbeError::Other(e.into()))?;
+        )?;
 
         tracing::info!("Found {} TAPs on reset scan", idcodes.len());
         tracing::debug!("Detected IR lens: {:?}", ir_lens);
diff --git a/probe-rs/src/probe/fake_probe.rs b/probe-rs/src/probe/fake_probe.rs
index 90bf2b4d..e8d364f5 100644
--- a/probe-rs/src/probe/fake_probe.rs
+++ b/probe-rs/src/probe/fake_probe.rs
@@ -319,9 +319,9 @@ impl DebugProbe for FakeProbe {
     fn scan_chain(&self) -> Result<&[ScanChainElement], DebugProbeError> {
         match &self.scan_chain {
             Some(chain) => Ok(chain),
-            None => Err(DebugProbeError::Other(anyhow::anyhow!(
-                "No scan chain set for fake probe"
-            ))),
+            None => Err(DebugProbeError::Other(
+                "No scan chain set for fake probe".to_string(),
+            )),
         }
     }
 
diff --git a/probe-rs/src/probe/ftdi/mod.rs b/probe-rs/src/probe/ftdi/mod.rs
index fa466e50..44b8eeb5 100644
--- a/probe-rs/src/probe/ftdi/mod.rs
+++ b/probe-rs/src/probe/ftdi/mod.rs
@@ -17,7 +17,6 @@ use crate::{
         ProbeCreationError, ProbeFactory, ScanChainElement, WireProtocol,
     },
 };
-use anyhow::anyhow;
 use bitvec::prelude::*;
 use nusb::DeviceInfo;
 use std::{
@@ -160,7 +159,7 @@ impl JtagAdapter {
         }
 
         if reply.len() != self.in_bit_counts.len() {
-            return Err(DebugProbeError::Other(anyhow!(
+            return Err(DebugProbeError::Other(format!(
                 "Read more data than expected. Expected {} bytes, got {} bytes",
                 self.in_bit_counts.len(),
                 reply.len()
diff --git a/probe-rs/src/probe/jlink/mod.rs b/probe-rs/src/probe/jlink/mod.rs
index e0136ec7..a7b4ca76 100644
--- a/probe-rs/src/probe/jlink/mod.rs
+++ b/probe-rs/src/probe/jlink/mod.rs
@@ -746,7 +746,7 @@ impl JLink {
         tracing::debug!("Buffer length for j-link transfer: {}", buf.len());
 
         if buf.len() > self.max_mem_block_size as usize {
-            return Err(DebugProbeError::Other(anyhow::anyhow!("Maximum transfer size for this probe is {} bytes, but current transfer is {} bytes", self.max_mem_block_size, buf.len())));
+            return Err(DebugProbeError::Other(format!("Maximum transfer size for this probe is {} bytes, but current transfer is {} bytes", self.max_mem_block_size, buf.len())));
         } else {
             tracing::debug!(
                 "Transferring {} bytes, max is {}",
diff --git a/probe-rs/src/semihosting.rs b/probe-rs/src/semihosting.rs
index c646feee..91cde06c 100644
--- a/probe-rs/src/semihosting.rs
+++ b/probe-rs/src/semihosting.rs
@@ -1,5 +1,4 @@
 use crate::{CoreInterface, Error, RegisterValue};
-use anyhow::{bail, Result};
 
 /// Indicates the operation the target would like the debugger to perform.
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -59,12 +58,12 @@ pub struct UnknownCommandDetails {
 
 impl UnknownCommandDetails {
     /// Returns the buffer pointed-to by the parameter of the semihosting operation
-    pub fn get_buffer(&self, core: &mut dyn CoreInterface) -> Result<Buffer> {
+    pub fn get_buffer(&self, core: &mut dyn CoreInterface) -> Result<Buffer, Error> {
         Buffer::from_block_at(core, self.parameter)
     }
 
     /// Writes the status of the semihosting operation to the return register of the target
-    pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<()> {
+    pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
         write_status(core, status)
     }
 }
@@ -79,7 +78,7 @@ impl GetCommandLineRequest {
         &self,
         core: &mut dyn CoreInterface,
         cmdline: &str,
-    ) -> Result<()> {
+    ) -> Result<(), Error> {
         let mut buf = cmdline.to_owned().into_bytes();
         buf.push(0);
         self.0.write(core, &buf)?;
@@ -91,7 +90,7 @@ impl GetCommandLineRequest {
     }
 }
 
-fn write_status(core: &mut dyn CoreInterface, value: i32) -> Result<()> {
+fn write_status(core: &mut dyn CoreInterface, value: i32) -> Result<(), crate::Error> {
     let reg = core.registers().get_argument_register(0).unwrap();
     core.write_core_reg(reg.into(), RegisterValue::U32(value as u32))?;
 
@@ -110,7 +109,10 @@ pub struct Buffer {
 
 impl Buffer {
     /// Constructs a new buffer, reading the address and length from the target.
-    pub fn from_block_at(core: &mut dyn CoreInterface, block_addr: u32) -> Result<Self> {
+    pub fn from_block_at(
+        core: &mut dyn CoreInterface,
+        block_addr: u32,
+    ) -> Result<Self, crate::Error> {
         let mut block: [u32; 2] = [0, 0];
         core.read_32(block_addr as u64, &mut block)?;
         Ok(Self {
@@ -121,7 +123,7 @@ impl Buffer {
     }
 
     /// Reads the buffer contents from the target.
-    pub fn read(&self, core: &mut dyn CoreInterface) -> Result<Vec<u8>> {
+    pub fn read(&self, core: &mut dyn CoreInterface) -> Result<Vec<u8>, Error> {
         let mut buf = vec![0u8; self.len as usize];
         core.read(self.address as u64, &mut buf[..])?;
         Ok(buf)
@@ -129,12 +131,12 @@ impl Buffer {
 
     /// Writes the passed buffer to the target buffer.
     /// The buffer must end with \0. Length written to target will not include \0.
-    pub fn write(&self, core: &mut dyn CoreInterface, buf: &[u8]) -> Result<()> {
+    pub fn write(&self, core: &mut dyn CoreInterface, buf: &[u8]) -> Result<(), Error> {
         if buf.len() > self.len as usize {
-            bail!("buffer not large enough")
+            return Err(Error::Other("buffer not large enough".to_string()));
         }
         if buf.last() != Some(&0) {
-            bail!("last byte of buffer must be 0");
+            return Err(Error::Other("last byte of buffer must be 0".to_string()));
         }
         core.write_8(self.address as u64, buf)?;
         let block: [u32; 2] = [self.address, (buf.len() - 1) as u32];
diff --git a/probe-rs/src/session.rs b/probe-rs/src/session.rs
index 0cb56f6f..7e5dd491 100644
--- a/probe-rs/src/session.rs
+++ b/probe-rs/src/session.rs
@@ -24,7 +24,6 @@ use crate::{
     probe::{list::Lister, AttachMethod, DebugProbeError, Probe},
     Core, CoreType, Error,
 };
-use anyhow::anyhow;
 use std::ops::DerefMut;
 use std::{fmt, sync::Arc, time::Duration};
 
@@ -177,7 +176,7 @@ impl Session {
         let default_core = target.default_core();
 
         let default_memory_ap = default_core.memory_ap().ok_or_else(|| {
-            Error::Other(anyhow::anyhow!(
+            Error::Other(format!(
                 "Unable to connect to core {default_core:?}, no memory AP configured"
             ))
         })?;
@@ -336,7 +335,7 @@ impl Session {
                     // Already initialised.
                     continue;
                 }
-                return Err(Error::Probe(DebugProbeError::Other(anyhow::anyhow!(
+                return Err(Error::Probe(DebugProbeError::Other(format!(
                     "{core_arch:?} core can not be mixed with a {debug_arch:?} debug module.",
                 ))));
             }
@@ -364,7 +363,7 @@ impl Session {
                     JtagInterface::Xtensa(state)
                 }
                 _ => {
-                    return Err(Error::Probe(DebugProbeError::Other(anyhow::anyhow!(
+                    return Err(Error::Probe(DebugProbeError::Other(format!(
                         "Unsupported core architecture {core_arch:?}",
                     ))));
                 }
@@ -633,20 +632,18 @@ impl Session {
     /// Err(e) if the custom erase sequence failed
     pub fn sequence_erase_all(&mut self) -> Result<(), Error> {
         let ArchitectureInterface::Arm(ref mut interface) = self.interfaces else {
-            return Err(Error::Probe(DebugProbeError::NotImplemented {
-                function_name: "Debug Erase Sequence",
-            }));
+            return Err(Error::NotImplemented(
+                "Debug Erase Sequence is not implemented for non-ARM targets.",
+            ));
         };
 
         let DebugSequence::Arm(ref debug_sequence) = self.target.debug_sequence else {
             unreachable!("This should never happen. Please file a bug if it does.");
         };
 
-        let Some(erase_sequence) = debug_sequence.debug_erase_sequence() else {
-            return Err(Error::Probe(DebugProbeError::NotImplemented {
-                function_name: "Debug Erase Sequence",
-            }));
-        };
+        let erase_sequence = debug_sequence
+            .debug_erase_sequence()
+            .ok_or(Error::Arm(ArmError::NotImplemented("Debug Erase Sequence")))?;
 
         tracing::info!("Trying Debug Erase Sequence");
         let erase_result = erase_sequence.erase_all(interface.deref_mut());
@@ -787,20 +784,14 @@ impl Drop for Session {
     #[tracing::instrument(name = "session_drop", skip(self))]
     fn drop(&mut self) {
         if let Err(err) = self.clear_all_hw_breakpoints() {
-            tracing::warn!(
-                "Could not clear all hardware breakpoints: {:?}",
-                anyhow!(err)
-            );
+            tracing::warn!("Could not clear all hardware breakpoints: {:?}", err);
         }
 
         // Call any necessary deconfiguration/shutdown hooks.
         if let Err(err) = { 0..self.cores.len() }
             .try_for_each(|i| self.core(i).and_then(|mut core| core.debug_core_stop()))
         {
-            tracing::warn!(
-                "Failed to deconfigure device during shutdown: {:?}",
-                anyhow!(err)
-            );
+            tracing::warn!("Failed to deconfigure device during shutdown: {:?}", err);
         }
     }
 }
diff --git a/probe-rs/src/vendor/microchip/sequences/atsam.rs b/probe-rs/src/vendor/microchip/sequences/atsam.rs
index 78c43097..caed9bd5 100644
--- a/probe-rs/src/vendor/microchip/sequences/atsam.rs
+++ b/probe-rs/src/vendor/microchip/sequences/atsam.rs
@@ -24,8 +24,6 @@ use std::{
     time::{Duration, Instant},
 };
 
-use anyhow::Result;
-
 bitfield! {
     /// Device Service Unit Control Register, DSU - CTRL
     #[derive(Copy, Clone)]
diff --git a/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs b/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
index ec4edd68..5280f038 100644
--- a/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
+++ b/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
@@ -231,7 +231,6 @@ impl MIMXRT11xx {
         reg: crate::core::registers::CoreRegister,
     ) -> Result<u32, ArmError> {
         crate::architecture::arm::core::cortex_m::read_core_reg(probe, reg.into())
-            .map_err(|err| ArmError::Other(err.into()))
     }
 
     /// Assumes that the core is halted.
@@ -241,8 +240,7 @@ impl MIMXRT11xx {
         reg: crate::core::registers::CoreRegister,
         value: u32,
     ) -> Result<(), ArmError> {
-        crate::architecture::arm::core::cortex_m::write_core_reg(probe, reg.into(), value)
-            .map_err(|err| ArmError::Other(err.into()))?;
+        crate::architecture::arm::core::cortex_m::write_core_reg(probe, reg.into(), value)?;
         probe.flush()?;
         Ok(())
     }


Test patch:
diff --git a/probe-rs/src/test.rs b/probe-rs/src/test.rs
index 7c0efe56..d3c5fc9a 100644
--- a/probe-rs/src/test.rs
+++ b/probe-rs/src/test.rs
@@ -78,30 +78,30 @@ impl MemoryInterface for MockMemory {
         false
     }
 
-    fn read_word_64(&mut self, _address: u64) -> anyhow::Result<u64, crate::Error> {
+    fn read_word_64(&mut self, _address: u64) -> Result<u64, crate::Error> {
         todo!()
     }
 
-    fn read_word_32(&mut self, address: u64) -> anyhow::Result<u32, crate::Error> {
+    fn read_word_32(&mut self, address: u64) -> Result<u32, crate::Error> {
         let mut bytes = [0u8; 4];
         self.read_8(address, &mut bytes)?;
 
         Ok(u32::from_le_bytes(bytes))
     }
 
-    fn read_word_8(&mut self, _address: u64) -> anyhow::Result<u8, crate::Error> {
+    fn read_word_8(&mut self, _address: u64) -> Result<u8, crate::Error> {
         todo!()
     }
 
-    fn read_word_16(&mut self, _address: u64) -> anyhow::Result<u16, crate::Error> {
+    fn read_word_16(&mut self, _address: u64) -> Result<u16, crate::Error> {
         todo!()
     }
 
-    fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> anyhow::Result<(), crate::Error> {
+    fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> anyhow::Result<(), crate::Error> {
+    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
         let mut buff = vec![0u8; data.len() * 4];
 
         self.read_8(address, &mut buff)?;
@@ -113,11 +113,11 @@ impl MemoryInterface for MockMemory {
         Ok(())
     }
 
-    fn read_16(&mut self, _address: u64, _data: &mut [u16]) -> anyhow::Result<(), crate::Error> {
+    fn read_16(&mut self, _address: u64, _data: &mut [u16]) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> anyhow::Result<(), crate::Error> {
+    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
         let stored_data = match self
             .values
             .binary_search_by_key(&address, |(addr, _data)| *addr)
@@ -164,39 +164,39 @@ impl MemoryInterface for MockMemory {
         Ok(true)
     }
 
-    fn write_word_64(&mut self, _address: u64, _data: u64) -> anyhow::Result<(), crate::Error> {
+    fn write_word_64(&mut self, _address: u64, _data: u64) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_word_32(&mut self, _address: u64, _data: u32) -> anyhow::Result<(), crate::Error> {
+    fn write_word_32(&mut self, _address: u64, _data: u32) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_word_16(&mut self, _address: u64, _data: u16) -> anyhow::Result<(), crate::Error> {
+    fn write_word_16(&mut self, _address: u64, _data: u16) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_word_8(&mut self, _address: u64, _data: u8) -> anyhow::Result<(), crate::Error> {
+    fn write_word_8(&mut self, _address: u64, _data: u8) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_64(&mut self, _address: u64, _data: &[u64]) -> anyhow::Result<(), crate::Error> {
+    fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_32(&mut self, _address: u64, _data: &[u32]) -> anyhow::Result<(), crate::Error> {
+    fn write_32(&mut self, _address: u64, _data: &[u32]) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_16(&mut self, _address: u64, _data: &[u16]) -> anyhow::Result<(), crate::Error> {
+    fn write_16(&mut self, _address: u64, _data: &[u16]) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn write_8(&mut self, _address: u64, _data: &[u8]) -> anyhow::Result<(), crate::Error> {
+    fn write_8(&mut self, _address: u64, _data: &[u8]) -> Result<(), crate::Error> {
         todo!()
     }
 
-    fn flush(&mut self) -> anyhow::Result<(), crate::Error> {
+    fn flush(&mut self) -> Result<(), crate::Error> {
         todo!()
     }
 }