diff --git a/crates/tor-guardmgr/src/err.rs b/crates/tor-guardmgr/src/err.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b7d56c4600efa39bea2c4b6e42529f8a59d3056c
--- /dev/null
+++ b/crates/tor-guardmgr/src/err.rs
@@ -0,0 +1,75 @@
+//! Declare error types for the `tor-guardmgr` crate.
+
+use futures::task::SpawnError;
+use std::sync::Arc;
+use std::time::Instant;
+use tor_error::{ErrorKind, HasKind};
+
+/// A error caused by a failure to pick a guard.
+#[derive(Clone, Debug, thiserror::Error)]
+#[non_exhaustive]
+pub enum PickGuardError {
+    /// All members of the current sample were down.
+    #[error("All guards are down")]
+    AllGuardsDown {
+        /// The next time at which any guard will be retriable.
+        retry_at: Option<Instant>,
+    },
+
+    /// Some guards were running, but all of them were either blocked on pending
+    /// circuits at other guards, unusable for the provided purpose, or filtered
+    /// out.
+    #[error("No running guards were usable for the selected purpose")]
+    NoGuardsUsable,
+}
+
+impl tor_error::HasKind for PickGuardError {
+    fn kind(&self) -> tor_error::ErrorKind {
+        use tor_error::ErrorKind as EK;
+        use PickGuardError as E;
+        match self {
+            E::AllGuardsDown { .. } => EK::TorAccessFailed,
+            E::NoGuardsUsable => EK::NoPath,
+        }
+    }
+}
+
+/// An error caused while creating or updating a guard manager.
+#[derive(Clone, Debug, thiserror::Error)]
+#[non_exhaustive]
+pub enum GuardMgrError {
+    /// An error manipulating persistent state
+    #[error("Problem accessing persistent state")]
+    State(#[from] tor_persist::Error),
+
+    /// An error that occurred while trying to spawn a daemon task.
+    #[error("Unable to spawn {spawning}")]
+    Spawn {
+        /// What we were trying to spawn.
+        spawning: &'static str,
+        /// What happened when we tried to spawn it.
+        #[source]
+        cause: Arc<SpawnError>,
+    },
+}
+
+impl HasKind for GuardMgrError {
+    #[rustfmt::skip] // to preserve table in match
+    fn kind(&self) -> ErrorKind {
+        use GuardMgrError as G;
+        match self {
+            G::State(e)               => e.kind(),
+            G::Spawn{ cause, .. }     => cause.kind(),
+        }
+    }
+}
+
+impl GuardMgrError {
+    /// Construct a new `GuardMgrError` from a `SpawnError`.
+    pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> GuardMgrError {
+        GuardMgrError::Spawn {
+            spawning,
+            cause: Arc::new(err),
+        }
+    }
+}
diff --git a/crates/tor-guardmgr/src/lib.rs b/crates/tor-guardmgr/src/lib.rs
index 11599299be2c07fd5c648834423ffef685771535..26e11f6dedfcdb24af92c701c5886c8c70b5bd93 100644
--- a/crates/tor-guardmgr/src/lib.rs
+++ b/crates/tor-guardmgr/src/lib.rs
@@ -132,7 +132,7 @@
 
 use educe::Educe;
 use futures::channel::mpsc;
-use futures::task::{SpawnError, SpawnExt};
+use futures::task::SpawnExt;
 use serde::{Deserialize, Serialize};
 use std::collections::{HashMap, HashSet};
 use std::convert::{TryFrom, TryInto};
@@ -141,13 +141,13 @@ use std::sync::{Arc, Mutex};
 use std::time::{Duration, Instant, SystemTime};
 use tracing::{debug, info, trace, warn};
 
-use tor_error::{ErrorKind, HasKind};
 use tor_llcrypto::pk;
 use tor_netdir::{params::NetParameters, NetDir, Relay};
 use tor_persist::{DynStorageHandle, StateMgr};
 use tor_rtcompat::Runtime;
 
 mod daemon;
+mod err;
 pub mod fallback;
 mod filter;
 mod guard;
@@ -155,9 +155,9 @@ mod pending;
 mod sample;
 mod util;
 
+pub use err::{GuardMgrError, PickGuardError};
 pub use filter::GuardFilter;
 pub use pending::{GuardMonitor, GuardStatus, GuardUsable};
-pub use sample::PickGuardError;
 
 use pending::{PendingRequest, RequestId};
 use sample::GuardSet;
@@ -1047,46 +1047,6 @@ pub enum GuardRestriction {
     AvoidAllIds(HashSet<pk::ed25519::Ed25519Identity>),
 }
 
-/// An error caused while creating or updating a guard manager.
-#[derive(Clone, Debug, thiserror::Error)]
-#[non_exhaustive]
-pub enum GuardMgrError {
-    /// An error manipulating persistent state
-    #[error("Problem accessing persistent state")]
-    State(#[from] tor_persist::Error),
-
-    /// An error that occurred while trying to spawn a daemon task.
-    #[error("Unable to spawn {spawning}")]
-    Spawn {
-        /// What we were trying to spawn.
-        spawning: &'static str,
-        /// What happened when we tried to spawn it.
-        #[source]
-        cause: Arc<SpawnError>,
-    },
-}
-
-impl HasKind for GuardMgrError {
-    #[rustfmt::skip] // to preserve table in match
-    fn kind(&self) -> ErrorKind {
-        use GuardMgrError as G;
-        match self {
-            G::State(e)               => e.kind(),
-            G::Spawn{ cause, .. }     => cause.kind(),
-        }
-    }
-}
-
-impl GuardMgrError {
-    /// Construct a new `GuardMgrError` from a `SpawnError`.
-    fn from_spawn(spawning: &'static str, err: SpawnError) -> GuardMgrError {
-        GuardMgrError::Spawn {
-            spawning,
-            cause: Arc::new(err),
-        }
-    }
-}
-
 #[cfg(test)]
 mod test {
     #![allow(clippy::unwrap_used)]
diff --git a/crates/tor-guardmgr/src/sample.rs b/crates/tor-guardmgr/src/sample.rs
index d620335e4fc159a62917cd3b5970e365f266ae4d..a4dcc056b3ccbd77e5c8edd1b8ea2c761914fcb9 100644
--- a/crates/tor-guardmgr/src/sample.rs
+++ b/crates/tor-guardmgr/src/sample.rs
@@ -3,7 +3,7 @@
 
 use crate::filter::GuardFilter;
 use crate::guard::{Guard, NewlyConfirmed, Reachable};
-use crate::{ExternalFailure, GuardId, GuardParams, GuardUsage, GuardUsageKind};
+use crate::{ExternalFailure, GuardId, GuardParams, GuardUsage, GuardUsageKind, PickGuardError};
 use tor_netdir::{NetDir, Relay};
 
 use itertools::Itertools;
@@ -781,35 +781,6 @@ impl<'a> From<GuardSample<'a>> for GuardSet {
     }
 }
 
-/// A error caused by a failure to pick a guard.
-#[derive(Clone, Debug, thiserror::Error)]
-#[non_exhaustive]
-pub enum PickGuardError {
-    /// All members of the current sample were down.
-    #[error("All guards are down")]
-    AllGuardsDown {
-        /// The next time at which any guard will be retriable.
-        retry_at: Option<Instant>,
-    },
-
-    /// Some guards were running, but all of them were either blocked on pending
-    /// circuits at other guards, unusable for the provided purpose, or filtered
-    /// out.
-    #[error("No running guards were usable for the selected purpose")]
-    NoGuardsUsable,
-}
-
-impl tor_error::HasKind for PickGuardError {
-    fn kind(&self) -> tor_error::ErrorKind {
-        use tor_error::ErrorKind as EK;
-        use PickGuardError as E;
-        match self {
-            E::AllGuardsDown { .. } => EK::TorAccessFailed,
-            E::NoGuardsUsable => EK::NoPath,
-        }
-    }
-}
-
 #[cfg(test)]
 mod test {
     #![allow(clippy::unwrap_used)]