about summary refs log tree commit diff stats
path: root/pkgs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs')
-rw-r--r--pkgs/by-name/mp/mpdpopm/Cargo.lock91
-rw-r--r--pkgs/by-name/mp/mpdpopm/Cargo.toml3
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs211
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs117
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/clients.rs309
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/config.rs34
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/filters_ast.rs227
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/lib.rs116
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/messages.rs165
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/playcounts.rs86
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/storage/mod.rs103
-rw-r--r--pkgs/by-name/mp/mpdpopm/src/vars.rs1
12 files changed, 215 insertions, 1248 deletions
diff --git a/pkgs/by-name/mp/mpdpopm/Cargo.lock b/pkgs/by-name/mp/mpdpopm/Cargo.lock
index fbedffd4..96909646 100644
--- a/pkgs/by-name/mp/mpdpopm/Cargo.lock
+++ b/pkgs/by-name/mp/mpdpopm/Cargo.lock
@@ -3,21 +3,6 @@
 version = 4
 
 [[package]]
-name = "addr2line"
-version = "0.25.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
-[[package]]
 name = "aho-corasick"
 version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -86,6 +71,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
 name = "ascii-canvas"
 version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -112,21 +103,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
 [[package]]
-name = "backtrace"
-version = "0.3.76"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-link",
-]
-
-[[package]]
 name = "bit-set"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -427,12 +403,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "gimli"
-version = "0.32.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
-
-[[package]]
 name = "hashbrown"
 version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -593,15 +563,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
 
 [[package]]
-name = "miniz_oxide"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
-dependencies = [
- "adler2",
-]
-
-[[package]]
 name = "mio"
 version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -616,8 +577,8 @@ dependencies = [
 name = "mpdpopm"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "async-trait",
- "backtrace",
  "boolinator",
  "chrono",
  "clap",
@@ -631,7 +592,6 @@ dependencies = [
  "regex",
  "serde",
  "serde_json",
- "snafu",
  "tokio",
  "toml",
  "tracing",
@@ -663,15 +623,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "object"
-version = "0.37.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
-dependencies = [
- "memchr",
-]
-
-[[package]]
 name = "once_cell"
 version = "1.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -835,12 +786,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
 
 [[package]]
-name = "rustc-demangle"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
-
-[[package]]
 name = "rustversion"
 version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -967,28 +912,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
 
 [[package]]
-name = "snafu"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2"
-dependencies = [
- "backtrace",
- "snafu-derive",
-]
-
-[[package]]
-name = "snafu-derive"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
 name = "socket2"
 version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pkgs/by-name/mp/mpdpopm/Cargo.toml b/pkgs/by-name/mp/mpdpopm/Cargo.toml
index ccadfabb..c82537e6 100644
--- a/pkgs/by-name/mp/mpdpopm/Cargo.toml
+++ b/pkgs/by-name/mp/mpdpopm/Cargo.toml
@@ -25,7 +25,6 @@ lalrpop = { version = "0.22", features = ["lexer"] }
 
 [dependencies]
 async-trait = "0.1"
-backtrace = "0.3"
 boolinator = "2.4"
 chrono = "0.4"
 clap = {version = "4.5", features = ["derive"]}
@@ -38,8 +37,8 @@ pin-project = "1.1"
 regex = "1.12"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.149"
-snafu = { version = "0.8.9", features = ["backtrace"] }
 toml = "0.9"
 tokio = { version = "1.49", features = ["io-util", "macros", "net", "process", "rt-multi-thread", "signal", "sync", "time"] }
 tracing = "0.1.44"
 tracing-subscriber = { version = "0.3.22", features = ["env-filter"]}
+anyhow = "1.0.100"
diff --git a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs
index 82a354d6..d9d607d5 100644
--- a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopm.rs
@@ -32,82 +32,12 @@ use mpdpopm::{
     storage::{last_played, play_count, rating_count},
 };
 
-use backtrace::Backtrace;
+use anyhow::{Context, Result, anyhow, bail};
 use clap::{Parser, Subcommand};
 use tracing::{debug, info, level_filters::LevelFilter, trace};
 use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt};
 
-use std::{fmt, path::PathBuf};
-
-#[non_exhaustive]
-pub enum Error {
-    NoSubCommand,
-    NoConfigArg,
-    NoRating,
-    NoPlayCount,
-    NoLastPlayed,
-    NoConfig {
-        config: std::path::PathBuf,
-        cause: std::io::Error,
-    },
-    PlayerStopped,
-    BadPath {
-        path: PathBuf,
-        back: Backtrace,
-    },
-    NoPlaylist,
-    Client {
-        source: mpdpopm::clients::Error,
-        back: Backtrace,
-    },
-    Ratings {
-        source: Box<mpdpopm::storage::Error>,
-        back: Backtrace,
-    },
-    Playcounts {
-        source: Box<mpdpopm::storage::Error>,
-        back: Backtrace,
-    },
-    ExpectedInt {
-        source: std::num::ParseIntError,
-        back: Backtrace,
-    },
-    Config {
-        source: crate::config::Error,
-        back: Backtrace,
-    },
-}
-
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::NoSubCommand => write!(f, "No sub-command given"),
-            Error::NoConfigArg => write!(f, "No argument given for the configuration option"),
-            Error::NoRating => write!(f, "No rating supplied"),
-            Error::NoPlayCount => write!(f, "No play count supplied"),
-            Error::NoLastPlayed => write!(f, "No last played timestamp given"),
-            Error::NoConfig { config, cause } => write!(f, "Bad config ({:?}): {}", config, cause),
-            Error::PlayerStopped => write!(f, "The player is stopped"),
-            Error::BadPath { path, back: _ } => write!(f, "Bad path: {:?}", path),
-            Error::NoPlaylist => write!(f, "No playlist given"),
-            Error::Client { source, back: _ } => write!(f, "Client error: {}", source),
-            Error::Ratings { source, back: _ } => write!(f, "Rating error: {}", source),
-            Error::Playcounts { source, back: _ } => write!(f, "Playcount error: {}", source),
-            Error::ExpectedInt { source, back: _ } => write!(f, "Expected integer: {}", source),
-            Error::Config { source, back: _ } => {
-                write!(f, "Error reading configuration: {}", source)
-            }
-        }
-    }
-}
-
-impl fmt::Debug for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self)
-    }
-}
-
-type Result<T> = std::result::Result<T, Error>;
+use std::path::PathBuf;
 
 /// Map `tracks' argument(s) to a Vec of String containing one or more mpd URIs
 ///
@@ -132,20 +62,18 @@ async fn provide_file(client: &mut Client, maybe_file: Option<String>) -> Result
     let file = match maybe_file {
         Some(file) => file,
         None => {
-            match client.status().await.map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })? {
+            match client
+                .status()
+                .await
+                .context("Failed to get status of client")?
+            {
                 PlayerStatus::Play(curr) | PlayerStatus::Pause(curr) => curr
                     .file
                     .to_str()
-                    .ok_or_else(|| Error::BadPath {
-                        path: curr.file.clone(),
-                        back: Backtrace::new(),
-                    })?
+                    .ok_or_else(|| anyhow!("Path is not utf8: `{}`", curr.file.display()))?
                     .to_string(),
                 PlayerStatus::Stopped => {
-                    return Err(Error::PlayerStopped);
+                    bail!("Player is stopped");
                 }
             }
         }
@@ -163,12 +91,7 @@ async fn get_ratings(
     let mut ratings: Vec<(String, i8)> = Vec::new();
 
     for file in map_tracks(client, tracks).await? {
-        let rating = rating_count::get(client, &file)
-            .await
-            .map_err(|err| Error::Ratings {
-                source: Box::new(err),
-                back: Backtrace::new(),
-            })?;
+        let rating = rating_count::get(client, &file).await?;
 
         ratings.push((file, rating.unwrap_or_default()));
     }
@@ -189,12 +112,7 @@ async fn set_rating(client: &mut Client, rating: i8, arg: Option<String>) -> Res
     let is_current = arg.is_none();
     let file = provide_file(client, arg).await?;
 
-    rating_count::set(client, &file, rating)
-        .await
-        .map_err(|err| Error::Ratings {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    rating_count::set(client, &file, rating).await?;
 
     match is_current {
         false => info!("Set the rating for \"{}\" to \"{}\".", file, rating),
@@ -209,19 +127,9 @@ async fn inc_rating(client: &mut Client, arg: Option<String>) -> Result<()> {
     let is_current = arg.is_none();
     let file = provide_file(client, arg).await?;
 
-    let now = rating_count::get(client, &file)
-        .await
-        .map_err(|err| Error::Ratings {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    let now = rating_count::get(client, &file).await?;
 
-    rating_count::set(client, &file, now.unwrap_or_default().saturating_add(1))
-        .await
-        .map_err(|err| Error::Ratings {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    rating_count::set(client, &file, now.unwrap_or_default().saturating_add(1)).await?;
 
     match is_current {
         false => info!("Incremented the rating for \"{}\".", file),
@@ -236,19 +144,9 @@ async fn decr_rating(client: &mut Client, arg: Option<String>) -> Result<()> {
     let is_current = arg.is_none();
     let file = provide_file(client, arg).await?;
 
-    let now = rating_count::get(client, &file)
-        .await
-        .map_err(|err| Error::Ratings {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    let now = rating_count::get(client, &file).await?;
 
-    rating_count::set(client, &file, now.unwrap_or_default().saturating_sub(1))
-        .await
-        .map_err(|err| Error::Ratings {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    rating_count::set(client, &file, now.unwrap_or_default().saturating_sub(1)).await?;
 
     match is_current {
         false => info!("Decremented the rating for \"{}\".", file),
@@ -266,13 +164,7 @@ async fn get_play_counts(
 ) -> Result<()> {
     let mut playcounts: Vec<(String, usize)> = Vec::new();
     for file in map_tracks(client, tracks).await? {
-        let playcount = play_count::get(client, &file)
-            .await
-            .map_err(|err| Error::Playcounts {
-                source: Box::new(err),
-                back: Backtrace::new(),
-            })?
-            .unwrap_or_default();
+        let playcount = play_count::get(client, &file).await?.unwrap_or_default();
         playcounts.push((file, playcount));
     }
 
@@ -292,12 +184,7 @@ async fn set_play_counts(client: &mut Client, playcount: usize, arg: Option<Stri
     let is_current = arg.is_none();
     let file = provide_file(client, arg).await?;
 
-    play_count::set(client, &file, playcount)
-        .await
-        .map_err(|err| Error::Playcounts {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    play_count::set(client, &file, playcount).await?;
 
     match is_current {
         false => info!("Set the playcount for \"{}\" to \"{}\".", file, playcount),
@@ -318,13 +205,7 @@ async fn get_last_playeds(
 ) -> Result<()> {
     let mut lastplayeds: Vec<(String, Option<u64>)> = Vec::new();
     for file in map_tracks(client, tracks).await? {
-        let lastplayed =
-            last_played::get(client, &file)
-                .await
-                .map_err(|err| Error::Playcounts {
-                    source: Box::new(err),
-                    back: Backtrace::new(),
-                })?;
+        let lastplayed = last_played::get(client, &file).await?;
         lastplayeds.push((file, lastplayed));
     }
 
@@ -357,12 +238,7 @@ async fn set_last_playeds(client: &mut Client, lastplayed: u64, arg: Option<Stri
     let is_current = arg.is_none();
     let file = provide_file(client, arg).await?;
 
-    last_played::set(client, &file, lastplayed)
-        .await
-        .map_err(|err| Error::Playcounts {
-            source: Box::new(err),
-            back: Backtrace::new(),
-        })?;
+    last_played::set(client, &file, lastplayed).await?;
 
     match is_current {
         false => info!("Set last played for \"{}\" to \"{}\".", file, lastplayed),
@@ -377,13 +253,7 @@ async fn set_last_playeds(client: &mut Client, lastplayed: u64, arg: Option<Stri
 
 /// Retrieve the list of stored playlists
 async fn get_playlists(client: &mut Client) -> Result<()> {
-    let mut pls = client
-        .get_stored_playlists()
-        .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+    let mut pls = client.get_stored_playlists().await?;
     pls.sort();
     println!("Stored playlists:");
     for pl in pls {
@@ -397,13 +267,7 @@ async fn findadd(client: &mut Client, chan: &str, filter: &str, case: bool) -> R
     let qfilter = quote(filter);
     debug!("findadd: got ``{}'', quoted to ``{}''.", filter, qfilter);
     let cmd = format!("{} {}", if case { "findadd" } else { "searchadd" }, qfilter);
-    client
-        .send_message(chan, &cmd)
-        .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+    client.send_message(chan, &cmd).await?;
     Ok(())
 }
 
@@ -419,11 +283,7 @@ async fn send_command(client: &mut Client, chan: &str, args: Vec<String>) -> Res
                 .join(" ")
                 .as_str(),
         )
-        .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+        .await?;
     Ok(())
 }
 
@@ -647,18 +507,17 @@ async fn main() -> Result<()> {
 
     let config = if let Some(configpath) = &args.config {
         match std::fs::read_to_string(configpath) {
-            Ok(text) => config::from_str(&text).map_err(|err| Error::Config {
-                source: err,
-                back: Backtrace::new(),
+            Ok(text) => config::from_str(&text).with_context(|| {
+                format!("Failed to parse config file at: `{}`", configpath.display())
             })?,
             Err(err) => {
                 // Either they did _not_, in which case they probably want to know that the config
                 // file they explicitly asked for does not exist, or there was some other problem,
                 // in which case we're out of options, anyway. Either way:
-                return Err(Error::NoConfig {
-                    config: PathBuf::from(configpath),
-                    cause: err,
-                });
+                bail!(
+                    "Failed to read config file at: `{}`, because: {err}",
+                    configpath.display()
+                )
             }
         }
     } else {
@@ -691,18 +550,10 @@ async fn main() -> Result<()> {
     trace!("logging configured.");
 
     let mut client = match config.conn {
-        config::Connection::Local { path } => {
-            Client::open(path).await.map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?
+        config::Connection::Local { path } => Client::open(path).await?,
+        config::Connection::TCP { host, port } => {
+            Client::connect(format!("{}:{}", host, port)).await?
         }
-        config::Connection::TCP { host, port } => Client::connect(format!("{}:{}", host, port))
-            .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?,
     };
 
     match args.command {
diff --git a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs
index e903774c..643611d6 100644
--- a/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/bin/mpdpopmd.rs
@@ -25,94 +25,17 @@
 //! the sticker database, by invoking external commands to keep your tags up-to-date (something
 //! along the lines of [mpdcron](https://alip.github.io/mpdcron)).
 
-use mpdpopm::config;
-use mpdpopm::config::Config;
-use mpdpopm::mpdpopm;
+use mpdpopm::{
+    config::{self, Config},
+    mpdpopm,
+};
 
-use backtrace::Backtrace;
+use anyhow::{Context, Result, bail};
 use clap::Parser;
 use tracing::{info, level_filters::LevelFilter};
 use tracing_subscriber::{EnvFilter, Layer, Registry, layer::SubscriberExt};
 
-use std::{fmt, io, path::PathBuf, sync::MutexGuard};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                 mppopmd application Error type                                 //
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#[non_exhaustive]
-pub enum Error {
-    NoConfigArg,
-    NoConfig {
-        config: std::path::PathBuf,
-        cause: std::io::Error,
-    },
-    Filter {
-        source: tracing_subscriber::filter::FromEnvError,
-        back: Backtrace,
-    },
-    Fork {
-        errno: errno::Errno,
-        back: Backtrace,
-    },
-    PathContainsNull {
-        back: Backtrace,
-    },
-    OpenLockFile {
-        errno: errno::Errno,
-        back: Backtrace,
-    },
-    LockFile {
-        errno: errno::Errno,
-        back: Backtrace,
-    },
-    WritePid {
-        errno: errno::Errno,
-        back: Backtrace,
-    },
-    Config {
-        source: crate::config::Error,
-        back: Backtrace,
-    },
-    MpdPopm {
-        source: Box<mpdpopm::Error>,
-        back: Backtrace,
-    },
-}
-
-impl std::fmt::Display for Error {
-    #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::NoConfigArg => write!(f, "No configuration file given"),
-            Error::NoConfig { config, cause } => {
-                write!(f, "Configuration error ({:?}): {}", config, cause)
-            }
-            Error::Fork { errno, back: _ } => write!(f, "When forking, got errno {}", errno),
-            Error::PathContainsNull { back: _ } => write!(f, "Path contains a null character"),
-            Error::OpenLockFile { errno, back: _ } => {
-                write!(f, "While opening lock file, got errno {}", errno)
-            }
-            Error::LockFile { errno, back: _ } => {
-                write!(f, "While locking the lock file, got errno {}", errno)
-            }
-            Error::WritePid { errno, back: _ } => {
-                write!(f, "While writing pid file, got errno {}", errno)
-            }
-            Error::Config { source, back: _ } => write!(f, "Configuration error: {}", source),
-            Error::MpdPopm { source, back: _ } => write!(f, "mpdpopm error: {}", source),
-            _ => write!(f, "Unknown mppopmd error"),
-        }
-    }
-}
-
-impl fmt::Debug for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self)
-    }
-}
-
-type Result = std::result::Result<(), Error>;
+use std::{io, path::PathBuf, sync::MutexGuard};
 
 pub struct MyMutexGuardWriter<'a>(MutexGuard<'a, std::fs::File>);
 
@@ -164,22 +87,21 @@ struct Args {
     debug: bool,
 }
 
-/// Entry point for `mppopmd'.
+/// Entry point for `mpdopmd'.
 ///
 /// Do *not* use the #[tokio::main] attribute here! If this program is asked to daemonize (the usual
 /// case), we will fork after tokio has started its thread pool, with disastrous consequences.
 /// Instead, stay synchronous until we've daemonized (or figured out that we don't need to), and
 /// only then fire-up the tokio runtime.
-fn main() -> Result {
+fn main() -> Result<()> {
     use mpdpopm::vars::VERSION;
 
     let args = Args::parse();
 
     let config = if let Some(cfgpath) = &args.config {
         match std::fs::read_to_string(cfgpath) {
-            Ok(text) => config::from_str(&text).map_err(|err| Error::Config {
-                source: err,
-                back: Backtrace::new(),
+            Ok(text) => config::from_str(&text).with_context(|| {
+                format!("Failed to parse config file at: `{}`", cfgpath.display())
             })?,
             // The config file (defaulted or not) either didn't exist, or we were unable to read its
             // contents...
@@ -187,10 +109,10 @@ fn main() -> Result {
                 // Either they did _not_, in which case they probably want to know that the config
                 // file they explicitly asked for does not exist, or there was some other problem,
                 // in which case we're out of options, anyway. Either way:
-                return Err(Error::NoConfig {
-                    config: PathBuf::from(cfgpath),
-                    cause: err,
-                });
+                bail!(
+                    "No config file could be read at: `{}`, because: {err}",
+                    cfgpath.display()
+                )
             }
         }
     } else {
@@ -208,10 +130,7 @@ fn main() -> Result {
     let filter = EnvFilter::builder()
         .with_default_directive(lf.into())
         .from_env()
-        .map_err(|err| Error::Filter {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+        .context("Failed to construct env filter")?;
 
     let formatter: Box<dyn Layer<Registry> + Send + Sync> = {
         Box::new(
@@ -226,8 +145,6 @@ fn main() -> Result {
 
     info!("mppopmd {VERSION} logging at level {lf:#?}.");
     let rt = tokio::runtime::Runtime::new().unwrap();
-    rt.block_on(mpdpopm(config)).map_err(|err| Error::MpdPopm {
-        source: Box::new(err),
-        back: Backtrace::new(),
-    })
+
+    rt.block_on(mpdpopm(config)).context("Main mpdpopm failed")
 }
diff --git a/pkgs/by-name/mp/mpdpopm/src/clients.rs b/pkgs/by-name/mp/mpdpopm/src/clients.rs
index 587063b2..b88e4041 100644
--- a/pkgs/by-name/mp/mpdpopm/src/clients.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/clients.rs
@@ -31,9 +31,9 @@
 //! re-issue the "idle" command. This crate however takes the approach of two channels (like
 //! [mpdfav](https://github.com/vincent-petithory/mpdfav)).
 
+use anyhow::{Context, Error, Result, anyhow, bail, ensure};
 use async_trait::async_trait;
 use regex::Regex;
-use snafu::{Backtrace, IntoError, OptionExt, ResultExt, prelude::*};
 use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
 use tokio::net::{TcpStream, ToSocketAddrs, UnixStream};
 use tracing::{debug, info};
@@ -49,92 +49,9 @@ use std::{
     str::FromStr,
 };
 
-// The Protocol error, below, gets used a *lot*; anywhere we receive a message from the MPD server
-// that "should" never happen. To help give a bit of context beyond a stack trace, I use this
-// enumeration of "operations"
-/// Enumerated list of MPD operations; used in Error::Protocol to distinguish which operation it was
-/// that elicited the protocol error.
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum Operation {
-    Connect,
-    Status,
-    GetSticker,
-    SetSticker,
-    SendToPlaylist,
-    SendMessage,
-    Update,
-    GetStoredPlaylists,
-    RspToUris,
-    GetStickers,
-    GetAllSongs,
-    Add,
-    Idle,
-    GetMessages,
-}
-
-impl std::fmt::Display for Operation {
-    #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Operation::Connect => write!(f, "Connect"),
-            Operation::Status => write!(f, "Status"),
-            Operation::GetSticker => write!(f, "GetSticker"),
-            Operation::SetSticker => write!(f, "SetSticker"),
-            Operation::SendToPlaylist => write!(f, "SendToPlaylist"),
-            Operation::SendMessage => write!(f, "SendMessage"),
-            Operation::Update => write!(f, "Update"),
-            Operation::GetStoredPlaylists => write!(f, "GetStoredPlaylists"),
-            Operation::RspToUris => write!(f, "RspToUris"),
-            Operation::GetStickers => write!(f, "GetStickers"),
-            Operation::GetAllSongs => write!(f, "GetAllSongs"),
-            Operation::Add => write!(f, "Add"),
-            Operation::Idle => write!(f, "Idle"),
-            Operation::GetMessages => write!(f, "GetMessages"),
-            _ => write!(f, "Unknown client operation"),
-        }
-    }
-}
-
-/// An MPD client error
-#[derive(Debug, Snafu)]
-#[non_exhaustive]
-pub enum Error {
-    #[snafu(display("Protocol error ({}): {}", op, msg))]
-    Protocol {
-        op: Operation,
-        msg: String,
-        backtrace: Backtrace,
-    },
-    #[snafu(display("Protocol errror ({}): {}", op, source))]
-    ProtocolConv {
-        op: Operation,
-        source: Box<dyn std::error::Error>,
-        backtrace: Backtrace,
-    },
-    #[snafu(display("I/O error: {}", source))]
-    Io {
-        source: std::io::Error,
-        backtrace: Backtrace,
-    },
-    #[snafu(display("Encoding error: {}", source))]
-    Encoding {
-        source: std::string::FromUtf8Error,
-        backtrace: Backtrace,
-    },
-    #[snafu(display("While converting sticker ``{}'': {}", sticker, source))]
-    StickerConversion {
-        sticker: String,
-        source: Box<dyn std::error::Error>,
-        backtrace: Backtrace,
-    },
-    #[snafu(display("``{}'' is not a recognized Idle subsystem", text))]
-    IdleSubSystem { text: String, backtrace: Backtrace },
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
+// Some default error context messages
+const ENCODING_SNAFU: &str = "Failed to interpete text as utf8";
+const IO_SNAFU: &str = "Failed read from mpd socket";
 
 /// A description of the current track, suitable for our purposes (as in, it only tracks the
 /// attributes needed for this module's functionality).
@@ -187,10 +104,6 @@ impl PlayerStatus {
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                           Connection                                           //
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
 /// A trait representing a simple, textual request/response protocol like that
 /// [employed](https://www.musicpd.org/doc/html/protocol.html) by [MPD](https://www.musicpd.org/):
 /// the caller sends a textual command & the server responds with a (perhaps multi-line) textual
@@ -310,7 +223,7 @@ where
         self.sock
             .write_all(format!("{}\n", msg).as_bytes())
             .await
-            .context(IoSnafu)?;
+            .context(IO_SNAFU)?;
         let mut buf = Vec::with_capacity(hint);
 
         // Given the request/response nature of the MPD protocol, our callers expect a complete
@@ -319,7 +232,7 @@ where
         let mut cb = 0; // # bytes read so far
         let mut more = true; // true as long as there is more to read
         while more {
-            cb += self.sock.read_buf(&mut buf).await.context(IoSnafu)?;
+            cb += self.sock.read_buf(&mut buf).await.context(IO_SNAFU)?;
 
             // The shortest complete response has three bytes. If the final byte in `buf' is not a
             // newline, then don't bother looking further.
@@ -347,7 +260,7 @@ where
         }
 
         // Only doing this to trouble-shoot issue 11
-        String::from_utf8(buf.clone()).context(EncodingSnafu)
+        String::from_utf8(buf.clone()).context(ENCODING_SNAFU)
     }
 }
 
@@ -357,15 +270,15 @@ where
     T: AsyncReadExt + AsyncWriteExt + Send + Unpin,
 {
     let mut buf = Vec::with_capacity(32);
-    let _cb = sock.read_buf(&mut buf).await.context(IoSnafu)?;
+    let _cb = sock.read_buf(&mut buf).await.context(IO_SNAFU)?;
+
     // Only doing this to trouble-shoot issue 11
-    let text = String::from_utf8(buf.clone()).context(EncodingSnafu)?;
+    let text = String::from_utf8(buf.clone()).context(ENCODING_SNAFU)?;
+
     ensure!(
         text.starts_with("OK MPD "),
-        ProtocolSnafu {
-            op: Operation::Connect,
-            msg: text.trim()
-        }
+        "failed to connect: {}",
+        text.trim()
     );
     info!("Connected {}.", text[7..].trim());
     Ok(text[7..].trim().to_string())
@@ -373,7 +286,7 @@ where
 
 impl MpdConnection<TcpStream> {
     pub async fn connect<A: ToSocketAddrs>(addr: A) -> Result<Box<dyn RequestResponse>> {
-        let mut sock = TcpStream::connect(addr).await.context(IoSnafu)?;
+        let mut sock = TcpStream::connect(addr).await.context(IO_SNAFU)?;
         let proto_ver = parse_connect_rsp(&mut sock).await?;
         Ok(Box::new(MpdConnection::<TcpStream> {
             sock,
@@ -385,7 +298,7 @@ impl MpdConnection<TcpStream> {
 impl MpdConnection<UnixStream> {
     // NTS: we have to box the return value because a `dyn RequestResponse` isn't Sized.
     pub async fn connect<P: AsRef<Path>>(pth: P) -> Result<Box<dyn RequestResponse>> {
-        let mut sock = UnixStream::connect(pth).await.context(IoSnafu)?;
+        let mut sock = UnixStream::connect(pth).await.context(IO_SNAFU)?;
         let proto_ver = parse_connect_rsp(&mut sock).await?;
         Ok(Box::new(MpdConnection::<UnixStream> {
             sock,
@@ -478,13 +391,7 @@ impl Client {
         // also don't want to depend on the order.
         let text = self.stream.req("status").await?;
 
-        let proto = || -> Error {
-            ProtocolSnafu {
-                op: Operation::Status,
-                msg: text.to_owned(),
-            }
-            .build()
-        };
+        let proto = || -> Error { anyhow!("Failed to parse mpd status output (with regexes)") };
 
         // I first thought to avoid the use (and cost) of regular expressions by just doing
         // sub-string searching on "state: ", but when I realized I needed to only match at the
@@ -507,12 +414,7 @@ impl Client {
                     .ok_or_else(proto)?
                     .as_str()
                     .parse::<u64>()
-                    .map_err(|err| {
-                        ProtocolConvSnafu {
-                            op: Operation::Status,
-                        }
-                        .into_error(Box::new(err))
-                    })?;
+                    .context("Failed to parse songid as u64")?;
 
                 let elapsed = RE_ELAPSED
                     .captures(&text)
@@ -521,12 +423,7 @@ impl Client {
                     .ok_or_else(proto)?
                     .as_str()
                     .parse::<f64>()
-                    .map_err(|err| {
-                        ProtocolConvSnafu {
-                            op: Operation::Status,
-                        }
-                        .into_error(Box::new(err))
-                    })?;
+                    .context("failed to parse `elapsed` as f64")?;
 
                 // navigate from `songid'-- don't send a "currentsong" message-- the current song
                 // could have changed
@@ -545,12 +442,7 @@ impl Client {
                     .ok_or_else(proto)?
                     .as_str()
                     .parse::<f64>()
-                    .map_err(|err| {
-                        ProtocolConvSnafu {
-                            op: Operation::Status,
-                        }
-                        .into_error(Box::new(err))
-                    })?;
+                    .context("Failed to parse `duration` as f64")?;
 
                 let curr = CurrentSong::new(songid, PathBuf::from(file), elapsed, duration);
 
@@ -560,11 +452,7 @@ impl Client {
                     Ok(PlayerStatus::Pause(curr))
                 }
             }
-            _ => ProtocolSnafu {
-                op: Operation::Status,
-                msg: state.to_owned(),
-            }
-            .fail(),
+            _ => bail!("Encountered unknow state `{}`", state),
         }
     }
 
@@ -586,24 +474,18 @@ impl Client {
             let s = text[prefix.len()..]
                 .split('\n')
                 .next()
-                .context(ProtocolSnafu {
-                    op: Operation::GetSticker,
-                    msg,
-                })?;
-            Ok(Some(T::from_str(s).map_err(|err| {
-                StickerConversionSnafu {
-                    sticker: sticker_name.to_owned(),
-                }
-                .into_error(Box::new(err))
+                .with_context(|| format!("Failed to parse `{}` as get_sticker response", text))?;
+            Ok(Some(T::from_str(s).with_context(|| {
+                format!(
+                    "Failed to parse sticker value as correct type: `{}`",
+                    sticker_name
+                )
             })?))
         } else {
             // ACK_ERROR_NO_EXIST = 50 (Ack.hxx:17)
             ensure!(
                 text.starts_with("ACK [50@0]"),
-                ProtocolSnafu {
-                    op: Operation::GetSticker,
-                    msg,
-                }
+                "Missing no sticker response"
             );
             Ok(None)
         }
@@ -626,13 +508,7 @@ impl Client {
         let text = self.stream.req(&msg).await?;
         debug!("Sent `{}'; got `{}'", &msg, &text);
 
-        ensure!(
-            text.starts_with("OK"),
-            ProtocolSnafu {
-                op: Operation::SetSticker,
-                msg
-            }
-        );
+        ensure!(text.starts_with("OK"), "Set sticker, not acknowledged");
         Ok(())
     }
 
@@ -641,13 +517,7 @@ impl Client {
         let msg = format!("playlistadd {} {}", quote(pl), quote(file));
         let text = self.stream.req(&msg).await?;
         debug!("Sent `{}'; got `{}'.", &msg, &text);
-        ensure!(
-            text.starts_with("OK"),
-            ProtocolSnafu {
-                op: Operation::SendToPlaylist,
-                msg
-            }
-        );
+        ensure!(text.starts_with("OK"), "send_to_playlist not acknowledged");
         Ok(())
     }
 
@@ -657,13 +527,7 @@ impl Client {
         let text = self.stream.req(&msg).await?;
         debug!("Sent `{}'; got `{}'.", &msg, &text);
 
-        ensure!(
-            text.starts_with("OK"),
-            ProtocolSnafu {
-                op: Operation::SendMessage,
-                msg: text
-            }
-        );
+        ensure!(text.starts_with("OK"), "Send_message not acknowledged");
         Ok(())
     }
 
@@ -683,20 +547,12 @@ impl Client {
         let prefix = "updating_db: ";
         ensure!(
             text.starts_with(prefix),
-            ProtocolSnafu {
-                op: Operation::Update,
-                msg: &text
-            }
+            "update response doesn't start with correct prefix"
         );
         text[prefix.len()..].split('\n').collect::<Vec<&str>>()[0]
             .to_string()
             .parse::<u64>()
-            .map_err(|err| {
-                ProtocolConvSnafu {
-                    op: Operation::Update,
-                }
-                .into_error(Box::new(err))
-            })
+            .context("Failed to treat update job id as u64")
     }
 
     /// Get the list of stored playlists
@@ -717,10 +573,7 @@ impl Client {
         // ACK...
         ensure!(
             !text.starts_with("ACK"),
-            ProtocolSnafu {
-                op: Operation::GetStoredPlaylists,
-                msg: text
-            }
+            "get_stored_playlists response not acknowledged"
         );
         Ok(text
             .lines()
@@ -742,13 +595,7 @@ impl Client {
         // or
         //
         // ACK...
-        ensure!(
-            !text.starts_with("ACK"),
-            ProtocolSnafu {
-                op: Operation::RspToUris,
-                msg: text.to_owned()
-            }
-        );
+        ensure!(!text.starts_with("ACK"), "rsp_to_uris not acknowledged");
         Ok(text
             .lines()
             .filter_map(|x| x.strip_prefix("file: ").map(String::from))
@@ -814,27 +661,15 @@ impl Client {
         // or
         //
         // ACK ...
-        ensure!(
-            !text.starts_with("ACK"),
-            ProtocolSnafu {
-                op: Operation::GetStickers,
-                msg: text,
-            }
-        );
+        ensure!(!text.starts_with("ACK"), "get_stickers not ACKed");
         let mut m = HashMap::new();
         let mut lines = text.lines();
         loop {
-            let file = lines.next().context(ProtocolSnafu {
-                op: Operation::GetStickers,
-                msg: text.to_owned(),
-            })?;
+            let file = lines.next().context("get_stickers no new line")?;
             if "OK" == file {
                 break;
             }
-            let val = lines.next().context(ProtocolSnafu {
-                op: Operation::GetStickers,
-                msg: text.to_owned(),
-            })?;
+            let val = lines.next().context("get_stickers no val")?;
 
             m.insert(
                 String::from(&file[6..]),
@@ -863,13 +698,7 @@ impl Client {
         // OK
         //
         // or "ACK..."
-        ensure!(
-            !text.starts_with("ACK"),
-            ProtocolSnafu {
-                op: Operation::GetAllSongs,
-                msg: text,
-            }
-        );
+        ensure!(!text.starts_with("ACK"), "get_all_songs not ACKed");
         Ok(text
             .lines()
             .filter_map(|x| x.strip_prefix("file: ").map(String::from))
@@ -881,13 +710,7 @@ impl Client {
         let text = self.stream.req(&msg).await?;
         debug!("Sent `{}'; got `{}'.", &msg, &text);
 
-        ensure!(
-            text.starts_with("OK"),
-            ProtocolSnafu {
-                op: Operation::Add,
-                msg: &text
-            }
-        );
+        ensure!(text.starts_with("OK"), "add not Oked");
         Ok(())
     }
 }
@@ -1161,10 +984,7 @@ impl TryFrom<&str> for IdleSubSystem {
         } else if x == "message" {
             Ok(IdleSubSystem::Message)
         } else {
-            Err(IdleSubSystemSnafu {
-                text: String::from(text),
-            }
-            .build())
+            bail!("{}", text)
         }
     }
 }
@@ -1227,13 +1047,7 @@ impl IdleClient {
     pub async fn subscribe(&mut self, chan: &str) -> Result<()> {
         let text = self.conn.req(&format!("subscribe {}", chan)).await?;
         debug!("Sent subscribe message for {}; got `{}'.", chan, text);
-        ensure!(
-            text.starts_with("OK"),
-            ProtocolSnafu {
-                op: Operation::Connect,
-                msg: &text
-            }
-        );
+        ensure!(text.starts_with("OK"), "subscribe not Ok: `{}`", text);
         debug!("Subscribed to {}.", chan);
         Ok(())
     }
@@ -1255,27 +1069,12 @@ impl IdleClient {
         //
         // We remain subscribed, but we need to send a new idle message.
 
-        ensure!(
-            text.starts_with("changed: "),
-            ProtocolSnafu {
-                op: Operation::Idle,
-                msg: &text
-            }
-        );
-        let idx = text.find('\n').context(ProtocolSnafu {
-            op: Operation::Idle,
-            msg: text.to_owned(),
-        })?;
+        ensure!(text.starts_with("changed: "), "idle not OK: `{}`", text);
+        let idx = text.find('\n').context("idle has no newline")?;
 
         let result = IdleSubSystem::try_from(&text[9..idx])?;
         let text = text[idx + 1..].to_string();
-        ensure!(
-            text.starts_with("OK"),
-            ProtocolSnafu {
-                op: Operation::Idle,
-                msg: &text
-            }
-        );
+        ensure!(text.starts_with("OK"), "idle not OKed");
 
         Ok(result)
     }
@@ -1308,13 +1107,7 @@ impl IdleClient {
         for line in text.lines() {
             match state {
                 State::Init => {
-                    ensure!(
-                        line.starts_with("channel: "),
-                        ProtocolSnafu {
-                            op: Operation::GetMessages,
-                            msg: line.to_owned()
-                        }
-                    );
+                    ensure!(line.starts_with("channel: "), "no `channel: ` given");
                     chan = String::from(&line[9..]);
                     state = State::Running;
                 }
@@ -1339,20 +1132,12 @@ impl IdleClient {
                         }
                         state = State::Finished;
                     } else {
-                        return Err(ProtocolSnafu {
-                            op: Operation::GetMessages,
-                            msg: text,
-                        }
-                        .build());
+                        bail!("Failed to get messages: `{}`", text)
                     }
                 }
                 State::Finished => {
                     // Should never be here!
-                    return Err(ProtocolSnafu {
-                        op: Operation::GetMessages,
-                        msg: String::from(line),
-                    }
-                    .build());
+                    bail!("Failed to get messages: `{}`", text)
                 }
             }
         }
diff --git a/pkgs/by-name/mp/mpdpopm/src/config.rs b/pkgs/by-name/mp/mpdpopm/src/config.rs
index 08509e47..e6c01599 100644
--- a/pkgs/by-name/mp/mpdpopm/src/config.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/config.rs
@@ -40,9 +40,10 @@
 //!       bundle-up all the errors, report 'em & urge the user to use the most recent version
 use crate::vars::{LOCALSTATEDIR, PREFIX};
 
+use anyhow::{Result, bail};
 use serde::{Deserialize, Serialize};
 
-use std::path::PathBuf;
+use std::{env, path::PathBuf};
 
 /// [mpdpopm](crate) can communicate with MPD over either a local Unix socket, or over regular TCP
 #[derive(Debug, Deserialize, PartialEq, Serialize)]
@@ -85,15 +86,6 @@ mod test_connection {
     }
 }
 
-impl std::default::Default for Connection {
-    fn default() -> Self {
-        Connection::TCP {
-            host: String::from("localhost"),
-            port: 6600,
-        }
-    }
-}
-
 /// This is the most recent `mppopmd` configuration struct.
 #[derive(Deserialize, Debug, Serialize)]
 #[serde(default)]
@@ -139,31 +131,11 @@ impl Default for Config {
     }
 }
 
-#[derive(Debug)]
-pub enum Error {
-    /// Failure to parse
-    ParseFail { err: Box<dyn std::error::Error> },
-}
-
-impl std::fmt::Display for Error {
-    #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::ParseFail { err } => write!(f, "Parse failure: {}", err),
-            _ => write!(f, "Unknown configuration error"),
-        }
-    }
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
 pub fn from_str(text: &str) -> Result<Config> {
     let cfg: Config = match serde_json::from_str(text) {
         Ok(cfg) => cfg,
         Err(err_outer) => {
-            return Err(Error::ParseFail {
-                err: Box::new(err_outer),
-            });
+            bail!("Failed to parse config: `{}`", err_outer)
         }
     };
     Ok(cfg)
diff --git a/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs b/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs
index 7d30739d..bd1a67d6 100644
--- a/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/filters_ast.rs
@@ -20,7 +20,7 @@
 use crate::clients::Client;
 use crate::storage::{last_played, play_count, rating_count};
 
-use backtrace::Backtrace;
+use anyhow::{Context, Error, Result, anyhow, bail};
 use boolinator::Boolinator;
 use chrono::prelude::*;
 use tracing::debug;
@@ -197,7 +197,6 @@ pub enum Expression {
 
 #[cfg(test)]
 mod smoke_tests {
-
     use super::*;
     use crate::filters::*;
 
@@ -315,10 +314,6 @@ mod smoke_tests {
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-//                                        evaluation logic                                        //
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum EvalOp {
     And,
@@ -336,92 +331,6 @@ impl std::fmt::Display for EvalOp {
     }
 }
 
-#[derive(Debug)]
-pub enum Error {
-    BadISO8601String {
-        text: Vec<u8>,
-        back: Backtrace,
-    },
-    ExpectQuoted {
-        text: String,
-        back: Backtrace,
-    },
-    FilterTypeErr {
-        text: String,
-        back: Backtrace,
-    },
-    InvalidOperand {
-        op: OpCode,
-        back: Backtrace,
-    },
-    OperatorOnStack {
-        op: EvalOp,
-        back: Backtrace,
-    },
-    RatingOverflow {
-        rating: usize,
-        back: Backtrace,
-    },
-    TooManyOperands {
-        num_ops: usize,
-        back: Backtrace,
-    },
-    NumericParse {
-        sticker: String,
-        source: std::num::ParseIntError,
-        back: Backtrace,
-    },
-    Client {
-        source: crate::clients::Error,
-        back: Backtrace,
-    },
-}
-
-impl std::fmt::Display for Error {
-    #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::BadISO8601String { text, back: _ } => {
-                write!(f, "Bad ISO8601 timestamp: ``{:?}''", text)
-            }
-            Error::ExpectQuoted { text, back: _ } => write!(f, "Expected quote: ``{}''", text),
-            Error::FilterTypeErr { text, back: _ } => {
-                write!(f, "Un-expected type in filter ``{}''", text)
-            }
-            Error::InvalidOperand { op, back: _ } => write!(f, "Invalid operand {}", op),
-            Error::OperatorOnStack { op, back: _ } => {
-                write!(f, "Operator {} left on parse stack", op)
-            }
-            Error::RatingOverflow { rating, back: _ } => write!(f, "Rating {} overflows", rating),
-            Error::TooManyOperands { num_ops, back: _ } => {
-                write!(f, "Too many operands ({})", num_ops)
-            }
-            Error::NumericParse {
-                sticker,
-                source,
-                back: _,
-            } => write!(f, "While parsing sticker {}, got {}", sticker, source),
-            Error::Client { source, back: _ } => write!(f, "Client error: {}", source),
-        }
-    }
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match &self {
-            Error::NumericParse {
-                sticker: _,
-                source,
-                back: _,
-            } => Some(source),
-            Error::Client { source, back: _ } => Some(source),
-            _ => None,
-        }
-    }
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
 fn peek(buf: &[u8]) -> Option<char> {
     match buf.len() {
         0 => None,
@@ -433,10 +342,7 @@ fn peek(buf: &[u8]) -> Option<char> {
 /// Pop a single byte off of `buf`
 fn take1(buf: &mut &[u8], i: usize) -> Result<()> {
     if i > buf.len() {
-        return Err(Error::BadISO8601String {
-            text: buf.to_vec(),
-            back: Backtrace::new(),
-        });
+        bail!("Bad iso-8601 string: `{:#?}`", buf);
     }
     let (_first, second) = buf.split_at(i);
     *buf = second;
@@ -447,26 +353,20 @@ fn take1(buf: &mut &[u8], i: usize) -> Result<()> {
 fn take2<T>(buf: &mut &[u8], i: usize) -> Result<T>
 where
     T: FromStr,
+    <T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
 {
     // 1. check len
     if i > buf.len() {
-        return Err(Error::BadISO8601String {
-            text: buf.to_vec(),
-            back: Backtrace::new(),
-        });
+        bail!("Bad iso-8601 string: `{:#?}`", buf);
     }
+
     let (first, second) = buf.split_at(i);
     *buf = second;
     // 2. convert to a string
-    let s = std::str::from_utf8(first).map_err(|_| Error::BadISO8601String {
-        text: buf.to_vec(),
-        back: Backtrace::new(),
-    })?;
+    let s = std::str::from_utf8(first).context("Bad iso-8601 string")?;
     // 3. parse as a T
-    s.parse::<T>().map_err(|_err| Error::BadISO8601String {
-        text: buf.to_vec(),
-        back: Backtrace::new(),
-    }) // Parse*Error => Error
+    s.parse::<T>()
+        .context("Failed to parse iso-8601 string as T")
 }
 
 /// Parse a timestamp in ISO 8601 format to a chrono DateTime instance
@@ -545,18 +445,12 @@ pub fn parse_iso_8601(buf: &mut &[u8]) -> Result<i64> {
                 return Ok(Utc
                     .with_ymd_and_hms(year, month, day, hour, minute, second)
                     .single()
-                    .ok_or(Error::BadISO8601String {
-                        text: buf.to_vec(),
-                        back: Backtrace::new(),
-                    })?
+                    .ok_or(anyhow!("bad iso-8601 string"))?
                     .timestamp());
             } else {
                 let next = peek(buf);
                 if next != Some('-') && next != Some('+') {
-                    return Err(Error::BadISO8601String {
-                        text: buf.to_vec(),
-                        back: Backtrace::new(),
-                    });
+                    bail!("bad iso-8601 string")
                 }
                 let west = next == Some('-');
                 take1(buf, 1)?;
@@ -573,29 +467,17 @@ pub fn parse_iso_8601(buf: &mut &[u8]) -> Result<i64> {
 
                 if west {
                     return Ok(FixedOffset::west_opt(hours * 3600 + minutes * 60)
-                        .ok_or(Error::BadISO8601String {
-                            text: buf.to_vec(),
-                            back: Backtrace::new(),
-                        })?
+                        .ok_or(anyhow!("Bad iso-8601 string"))?
                         .with_ymd_and_hms(year, month, day, hour, minute, second)
                         .single()
-                        .ok_or(Error::BadISO8601String {
-                            text: buf.to_vec(),
-                            back: Backtrace::new(),
-                        })?
+                        .ok_or(anyhow!("Bad iso-8601 string"))?
                         .timestamp());
                 } else {
                     return Ok(FixedOffset::east_opt(hours * 3600 + minutes * 60)
-                        .ok_or(Error::BadISO8601String {
-                            text: buf.to_vec(),
-                            back: Backtrace::new(),
-                        })?
+                        .ok_or(anyhow!("Bad iso-8601 string"))?
                         .with_ymd_and_hms(year, month, day, hour, minute, second)
                         .single()
-                        .ok_or(Error::BadISO8601String {
-                            text: buf.to_vec(),
-                            back: Backtrace::new(),
-                        })?
+                        .ok_or(anyhow!("Bad iso-8601 string"))?
                         .timestamp());
                 }
             }
@@ -604,10 +486,7 @@ pub fn parse_iso_8601(buf: &mut &[u8]) -> Result<i64> {
     Ok(Local
         .with_ymd_and_hms(year, month, day, hour, minute, second)
         .single()
-        .ok_or(Error::BadISO8601String {
-            text: buf.to_vec(),
-            back: Backtrace::new(),
-        })?
+        .ok_or(anyhow!("Bad iso-8601 string"))?
         .timestamp())
 }
 
@@ -658,10 +537,7 @@ pub fn expect_quoted(qtext: &str) -> Result<String> {
     }
 
     if quote != Some('\'') && quote != Some('"') {
-        return Err(Error::ExpectQuoted {
-            text: String::from(qtext),
-            back: Backtrace::new(),
-        });
+        bail!("Expected text to be quoted: `{}`", qtext);
     }
 
     let mut ret = String::new();
@@ -676,10 +552,7 @@ pub fn expect_quoted(qtext: &str) -> Result<String> {
         match this {
             Some(c) => ret.push(c),
             None => {
-                return Err(Error::ExpectQuoted {
-                    text: String::from(qtext),
-                    back: Backtrace::new(),
-                });
+                bail!("Expected text to be quoted: `{}`", qtext);
             }
         }
         this = iter.next();
@@ -727,10 +600,7 @@ fn make_numeric_closure<'a, T: 'a + PartialEq + PartialOrd + Copy>(
         OpCode::LessThan => Ok(Box::new(move |x: T| x < val) as Box<dyn Fn(T) -> bool>),
         OpCode::GreaterThanEqual => Ok(Box::new(move |x: T| x >= val) as Box<dyn Fn(T) -> bool>),
         OpCode::LessThanEqual => Ok(Box::new(move |x: T| x <= val) as Box<dyn Fn(T) -> bool>),
-        _ => Err(Error::InvalidOperand {
-            op,
-            back: Backtrace::new(),
-        }),
+        _ => bail!("Invalid operant: `{op}`"),
     }
 }
 
@@ -767,18 +637,11 @@ async fn eval_numeric_sticker_term<
     let mut m = client
         .get_stickers(sticker)
         .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?
+        .context("Failed to get stickers from client")?
         .drain()
         .map(|(k, v)| v.parse::<T>().map(|x| (k, x)))
         .collect::<std::result::Result<HashMap<String, T>, _>>()
-        .map_err(|err| Error::NumericParse {
-            sticker: String::from(sticker),
-            source: err,
-            back: Backtrace::new(),
-        })?;
+        .context("Failed to parse sticker as T")?;
     // `m' is now a map of song URI to rating/playcount/wathever (expressed as a T)... for all songs
     // that have the salient sticker.
     //
@@ -787,10 +650,7 @@ async fn eval_numeric_sticker_term<
     client
         .get_all_songs()
         .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?
+        .context("Failed to get all songs from client")?
         .drain(..)
         .for_each(|song| {
             m.entry(song).or_insert(default_val);
@@ -846,10 +706,7 @@ async fn eval_term<'a>(
         Term::UnaryCondition(op, val) => Ok(client
             .find1(&format!("{}", op), &quote_value(val), case)
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?
+            .context("Failed to find1 on client")?
             .drain(..)
             .collect()),
         Term::BinaryCondition(attr, op, val) => {
@@ -857,20 +714,14 @@ async fn eval_term<'a>(
                 match val {
                     Value::Uint(n) => {
                         if *n > 255 {
-                            return Err(Error::RatingOverflow {
-                                rating: *n,
-                                back: Backtrace::new(),
-                            });
+                            bail!("Rating of `{}` is greater than allowed!", n)
                         }
                         Ok(
                             eval_numeric_sticker_term(stickers.rating, client, *op, *n as u8, 0)
                                 .await?,
                         )
                     }
-                    _ => Err(Error::FilterTypeErr {
-                        text: format!("filter ratings expect an unsigned int; got {:#?}", val),
-                        back: Backtrace::new(),
-                    }),
+                    _ => bail!("filter ratings expect an unsigned int; got {:#?}", val),
                 }
             } else if *attr == Selector::PlayCount {
                 match val {
@@ -880,10 +731,7 @@ async fn eval_term<'a>(
                                 .await?,
                         )
                     }
-                    _ => Err(Error::FilterTypeErr {
-                        text: format!("filter ratings expect an unsigned int; got {:#?}", val),
-                        back: Backtrace::new(),
-                    }),
+                    _ => bail!("filter ratings expect an unsigned int; got {:#?}", val),
                 }
             } else if *attr == Selector::LastPlayed {
                 match val {
@@ -893,10 +741,7 @@ async fn eval_term<'a>(
                                 .await?,
                         )
                     }
-                    _ => Err(Error::FilterTypeErr {
-                        text: format!("filter ratings expect an unsigned int; got {:#?}", val),
-                        back: Backtrace::new(),
-                    }),
+                    _ => bail!("filter ratings expect an unsigned int; got {:#?}", val),
                 }
             } else {
                 Ok(client
@@ -907,10 +752,7 @@ async fn eval_term<'a>(
                         case,
                     )
                     .await
-                    .map_err(|err| Error::Client {
-                        source: err,
-                        back: Backtrace::new(),
-                    })?
+                    .context("Failed to `find2` on client")?
                     .drain(..)
                     .collect())
             }
@@ -932,10 +774,7 @@ async fn negate_result(
     Ok(client
         .get_all_songs()
         .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?
+        .context("Failed to get all songs from client")?
         .drain(..)
         .filter_map(|song| {
             // Some(thing) adds thing, None elides it
@@ -1117,10 +956,7 @@ pub async fn evaluate<'a>(
         se.iter()
             .enumerate()
             .for_each(|(i, x)| debug!("    {}: {:#?}", i, x));
-        return Err(Error::TooManyOperands {
-            num_ops: se.len(),
-            back: Backtrace::new(),
-        });
+        bail!("The number of operants is too big `{}`", se.len());
     }
 
     let ret = se.pop().unwrap();
@@ -1128,10 +964,7 @@ pub async fn evaluate<'a>(
         EvalStackNode::Result(result) => Ok(result),
         EvalStackNode::Op(op) => {
             debug!("Operator left on stack (!?): {:#?}", op);
-            Err(Error::OperatorOnStack {
-                op,
-                back: Backtrace::new(),
-            })
+            bail!("Operator left on stack: {op}")
         }
     }
 }
diff --git a/pkgs/by-name/mp/mpdpopm/src/lib.rs b/pkgs/by-name/mp/mpdpopm/src/lib.rs
index e4579db2..4fe523ea 100644
--- a/pkgs/by-name/mp/mpdpopm/src/lib.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/lib.rs
@@ -49,14 +49,15 @@ pub mod filters {
     include!(concat!(env!("OUT_DIR"), "/src/filters.rs"));
 }
 
-use clients::{Client, IdleClient, IdleSubSystem};
-use config::Config;
-use config::Connection;
-use filters_ast::FilterStickerNames;
-use messages::MessageProcessor;
-use playcounts::PlayState;
+use crate::{
+    clients::{Client, IdleClient, IdleSubSystem},
+    config::{Config, Connection},
+    filters_ast::FilterStickerNames,
+    messages::MessageProcessor,
+    playcounts::PlayState,
+};
 
-use backtrace::Backtrace;
+use anyhow::{Context, Error};
 use futures::{future::FutureExt, pin_mut, select};
 use tokio::{
     signal,
@@ -65,100 +66,39 @@ use tokio::{
 };
 use tracing::{debug, error, info};
 
-use std::path::PathBuf;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#[derive(Debug)]
-#[non_exhaustive]
-pub enum Error {
-    BadPath {
-        pth: PathBuf,
-    },
-    Client {
-        source: crate::clients::Error,
-        back: Backtrace,
-    },
-    Playcounts {
-        source: crate::playcounts::Error,
-        back: Backtrace,
-    },
-}
-
-impl std::fmt::Display for Error {
-    #[allow(unreachable_patterns)] // the _ arm is *currently* unreachable
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth),
-            Error::Client { source, back: _ } => write!(f, "Client error: {}", source),
-            Error::Playcounts { source, back: _ } => write!(f, "Playcount error: {}", source),
-        }
-    }
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match &self {
-            Error::Client { source, back: _ } => Some(source),
-            _ => None,
-        }
-    }
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
 /// Core `mppopmd' logic
 pub async fn mpdpopm(cfg: Config) -> std::result::Result<(), Error> {
     info!("mpdpopm {} beginning.", vars::VERSION);
 
     let filter_stickers = FilterStickerNames::new();
 
-    let mut client = match cfg.conn {
-        Connection::Local { ref path } => {
-            Client::open(path).await.map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?
-        }
-        Connection::TCP { ref host, port } => Client::connect(format!("{}:{}", host, port))
-            .await
-            .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?,
-    };
+    let mut client =
+        match cfg.conn {
+            Connection::Local { ref path } => Client::open(path)
+                .await
+                .with_context(|| format!("Failed to open socket at `{}`", path.display()))?,
+            Connection::TCP { ref host, port } => Client::connect(format!("{}:{}", host, port))
+                .await
+                .with_context(|| format!("Failed to connect to client at `{}:{}`", host, port))?,
+        };
 
     let mut state = PlayState::new(&mut client, cfg.played_thresh)
         .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+        .context("Failed to construct PlayState")?;
 
     let mut idle_client = match cfg.conn {
-        Connection::Local { ref path } => {
-            IdleClient::open(path).await.map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?
-        }
+        Connection::Local { ref path } => IdleClient::open(path)
+            .await
+            .context("Failed to open idle client")?,
         Connection::TCP { ref host, port } => IdleClient::connect(format!("{}:{}", host, port))
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?,
+            .context("Failed to connect to TCP idle client")?,
     };
 
     idle_client
         .subscribe(&cfg.commands_chan)
         .await
-        .map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+        .context("Failed to subscribe to idle_client")?;
 
     let mut hup = signal(SignalKind::hangup()).unwrap();
     let mut kill = signal(SignalKind::terminate()).unwrap();
@@ -200,10 +140,7 @@ pub async fn mpdpopm(cfg: Config) -> std::result::Result<(), Error> {
                         tick.set(sleep(Duration::from_millis(cfg.poll_interval_ms)).fuse());
                         state.update(&mut client)
                                 .await
-                                .map_err(|err| Error::Playcounts {
-                                    source: err,
-                                    back: Backtrace::new()
-                                })?
+                                .context("PlayState update failed")?
                     },
                     // next = cmds.next() => match next {
                     //     Some(out) => {
@@ -229,10 +166,7 @@ pub async fn mpdpopm(cfg: Config) -> std::result::Result<(), Error> {
                             if subsys == IdleSubSystem::Player {
                                 state.update(&mut client)
                                     .await
-                                    .map_err(|err| Error::Playcounts {
-                                        source: err,
-                                        back: Backtrace::new()
-                                    })?
+                                    .context("PlayState update failed")?
                             } else if subsys == IdleSubSystem::Message {
                                 msg_check_needed = true;
                             }
diff --git a/pkgs/by-name/mp/mpdpopm/src/messages.rs b/pkgs/by-name/mp/mpdpopm/src/messages.rs
index c7c295c8..171a246a 100644
--- a/pkgs/by-name/mp/mpdpopm/src/messages.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/messages.rs
@@ -54,110 +54,11 @@ use crate::{
     filters_ast::{FilterStickerNames, evaluate},
 };
 
-use backtrace::Backtrace;
+use anyhow::{Context, Error, Result, anyhow, bail};
 use boolinator::Boolinator;
 use tracing::debug;
 
 use std::collections::VecDeque;
-use std::path::PathBuf;
-
-#[derive(Debug)]
-pub enum Error {
-    BadPath {
-        pth: PathBuf,
-    },
-    FilterParseError {
-        msg: String,
-    },
-    InvalidChar {
-        c: u8,
-    },
-    NoClosingQuotes,
-    NoCommand,
-    NotImplemented {
-        feature: String,
-    },
-    PlayerStopped,
-    TrailingBackslash,
-    UnknownChannel {
-        chan: String,
-        back: Backtrace,
-    },
-    UnknownCommand {
-        name: String,
-        back: Backtrace,
-    },
-    Client {
-        source: crate::clients::Error,
-        back: Backtrace,
-    },
-    Ratings {
-        source: crate::storage::Error,
-        back: Backtrace,
-    },
-    Playcount {
-        source: crate::storage::Error,
-        back: Backtrace,
-    },
-    Filter {
-        source: crate::filters_ast::Error,
-        back: Backtrace,
-    },
-    Utf8 {
-        source: std::str::Utf8Error,
-        buf: Vec<u8>,
-        back: Backtrace,
-    },
-    ExpectedInt {
-        source: std::num::ParseIntError,
-        text: String,
-        back: Backtrace,
-    },
-}
-
-impl std::fmt::Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth),
-            Error::FilterParseError { msg } => write!(f, "Parse error: ``{}''", msg),
-            Error::InvalidChar { c } => write!(f, "Invalid unquoted character {}", c),
-            Error::NoClosingQuotes => write!(f, "Missing closing quotes"),
-            Error::NoCommand => write!(f, "No command specified"),
-            Error::NotImplemented { feature } => write!(f, "`{}' not implemented, yet", feature),
-            Error::PlayerStopped => write!(
-                f,
-                "Can't operate on the current track when the player is stopped"
-            ),
-            Error::TrailingBackslash => write!(f, "Trailing backslash"),
-            Error::UnknownChannel { chan, back: _ } => write!(
-                f,
-                "We received messages for an unknown channel `{}'; this is likely a bug; please consider filing a report to sp1ff@pobox.com",
-                chan
-            ),
-            Error::UnknownCommand { name, back: _ } => {
-                write!(f, "We received an unknown message ``{}''", name)
-            }
-            Error::Client { source, back: _ } => write!(f, "Client error: {}", source),
-            Error::Ratings { source, back: _ } => write!(f, "Ratings eror: {}", source),
-            Error::Playcount { source, back: _ } => write!(f, "Playcount error: {}", source),
-            Error::Filter { source, back: _ } => write!(f, "Filter error: {}", source),
-            Error::Utf8 {
-                source,
-                buf,
-                back: _,
-            } => write!(f, "UTF8 error {} ({:#?})", source, buf),
-            Error::ExpectedInt {
-                source,
-                text,
-                back: _,
-            } => write!(f, "``{}''L {}", source, text),
-        }
-    }
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
 
 /// Break `buf` up into individual tokens while removing MPD-style quoting.
 ///
@@ -243,14 +144,14 @@ impl<'a> Iterator for TokenIterator<'a> {
                 if '\\' == self.slice[inp] as char {
                     inp += 1;
                     if inp == nslice {
-                        return Some(Err(Error::TrailingBackslash));
+                        return Some(Err(anyhow!("Trailing backslash")));
                     }
                 }
                 self.slice[out] = self.slice[inp];
                 out += 1;
                 inp += 1;
                 if inp == nslice {
-                    return Some(Err(Error::NoClosingQuotes));
+                    return Some(Err(anyhow!("No closing quote")));
                 }
             }
             // The next token is in self.slice[self.input..out] and self.slice[inp] is "
@@ -273,7 +174,7 @@ impl<'a> Iterator for TokenIterator<'a> {
                     break;
                 }
                 if self.slice[i] as char == '"' || self.slice[i] as char == '\'' {
-                    return Some(Err(Error::InvalidChar { c: self.slice[i] }));
+                    return Some(Err(anyhow!("Invalid char: `{}`", self.slice[i])));
                 }
                 i += 1;
             }
@@ -316,17 +217,11 @@ impl MessageProcessor {
         let m = idle_client
             .get_messages()
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?;
+            .context("Failed to `get_messages` from client")?;
 
         for (chan, msgs) in m {
             // Only supporting a single channel, ATM
-            (chan == command_chan).ok_or_else(|| Error::UnknownChannel {
-                chan,
-                back: Backtrace::new(),
-            })?;
+            (chan == command_chan).ok_or_else(|| anyhow!("Unknown chanell: `{}`", chan))?;
             for msg in msgs {
                 self.process(msg, client, &state, stickers).await?;
             }
@@ -365,11 +260,8 @@ impl MessageProcessor {
         let mut buf = msg.into_bytes();
         let args: VecDeque<&str> = tokenize(&mut buf)
             .map(|r| match r {
-                Ok(buf) => Ok(std::str::from_utf8(buf).map_err(|err| Error::Utf8 {
-                    source: err,
-                    buf: buf.to_vec(),
-                    back: Backtrace::new(),
-                })?),
+                Ok(buf) => Ok(std::str::from_utf8(buf)
+                    .context("Failed to interpete `findadd` string as utf8")?),
                 Err(err) => Err(err),
             })
             .collect::<Result<VecDeque<&str>>>()?;
@@ -385,9 +277,7 @@ impl MessageProcessor {
         let ast = match ExpressionParser::new().parse(args[0]) {
             Ok(ast) => ast,
             Err(err) => {
-                return Err(Error::FilterParseError {
-                    msg: format!("{}", err),
-                });
+                bail!("Failed to parse filter: `{}`", err)
             }
         };
 
@@ -396,22 +286,16 @@ impl MessageProcessor {
         let mut results = Vec::new();
         for song in evaluate(&ast, true, client, stickers)
             .await
-            .map_err(|err| Error::Filter {
-                source: err,
-                back: Backtrace::new(),
-            })?
+            .context("Failed to evaluate filter")?
         {
             results.push(client.add(&song).await);
         }
         match results
             .into_iter()
-            .collect::<std::result::Result<Vec<()>, crate::clients::Error>>()
+            .collect::<std::result::Result<Vec<()>, Error>>()
         {
             Ok(_) => Ok(()),
-            Err(err) => Err(Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            }),
+            Err(err) => Err(err),
         }
     }
 
@@ -427,14 +311,11 @@ impl MessageProcessor {
         let mut buf = msg.into_bytes();
         let args: VecDeque<&str> = tokenize(&mut buf)
             .map(|r| match r {
-                Ok(buf) => Ok(std::str::from_utf8(buf).map_err(|err| Error::Utf8 {
-                    source: err,
-                    buf: buf.to_vec(),
-                    back: Backtrace::new(),
-                })?),
+                Ok(buf) => Ok(std::str::from_utf8(buf)
+                    .context("Failed to interpete `searchadd` string as utf8")?),
                 Err(err) => Err(err),
             })
-            .collect::<Result<VecDeque<&str>>>()?;
+            .collect::<Result<VecDeque<_>>>()?;
 
         debug!("searchadd arguments: {:#?}", args);
 
@@ -447,9 +328,7 @@ impl MessageProcessor {
         let ast = match ExpressionParser::new().parse(args[0]) {
             Ok(ast) => ast,
             Err(err) => {
-                return Err(Error::FilterParseError {
-                    msg: format!("{}", err),
-                });
+                bail!("Failed to parse filter: `{err}`")
             }
         };
 
@@ -458,22 +337,16 @@ impl MessageProcessor {
         let mut results = Vec::new();
         for song in evaluate(&ast, false, client, stickers)
             .await
-            .map_err(|err| Error::Filter {
-                source: err,
-                back: Backtrace::new(),
-            })?
+            .context("Failed to evaluate ast")?
         {
             results.push(client.add(&song).await);
         }
         match results
             .into_iter()
-            .collect::<std::result::Result<Vec<()>, crate::clients::Error>>()
+            .collect::<std::result::Result<Vec<()>, Error>>()
         {
             Ok(_) => Ok(()),
-            Err(err) => Err(Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            }),
+            Err(err) => Err(err),
         }
     }
 }
diff --git a/pkgs/by-name/mp/mpdpopm/src/playcounts.rs b/pkgs/by-name/mp/mpdpopm/src/playcounts.rs
index 6ae8f903..7d646b4c 100644
--- a/pkgs/by-name/mp/mpdpopm/src/playcounts.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/playcounts.rs
@@ -26,67 +26,13 @@
 //!
 
 use crate::clients::{Client, PlayerStatus};
-use crate::storage::{self, last_played, play_count, skipped};
+use crate::storage::{last_played, play_count, skipped};
 
-use backtrace::Backtrace;
+use anyhow::{Context, Error, Result, anyhow};
 use tracing::{debug, info};
 
-use std::path::PathBuf;
 use std::time::SystemTime;
 
-#[derive(Debug)]
-pub enum Error {
-    PlayerStopped,
-    BadPath {
-        pth: PathBuf,
-    },
-    SystemTime {
-        source: std::time::SystemTimeError,
-        back: Backtrace,
-    },
-    Client {
-        source: crate::clients::Error,
-        back: Backtrace,
-    },
-}
-
-impl std::fmt::Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::PlayerStopped => write!(f, "The MPD player is stopped"),
-            Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth),
-            Error::SystemTime { source, back: _ } => {
-                write!(f, "Couldn't get system time: {}", source)
-            }
-            Error::Client { source, back: _ } => write!(f, "Client error: {}", source),
-        }
-    }
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match &self {
-            Error::SystemTime { source, back: _ } => Some(source),
-            Error::Client { source, back: _ } => Some(source),
-            _ => None,
-        }
-    }
-}
-
-impl From<storage::Error> for Error {
-    fn from(value: storage::Error) -> Self {
-        match value {
-            storage::Error::PlayerStopped => Self::PlayerStopped,
-            storage::Error::BadPath { pth } => Self::BadPath { pth },
-            storage::Error::SystemTime { source, back } => Self::SystemTime { source, back },
-            storage::Error::Client { source, back } => Self::Client { source, back },
-            _ => unreachable!(),
-        }
-    }
-}
-
-type Result<T> = std::result::Result<T, Error>;
-
 /// Current server state in terms of the play status (stopped/paused/playing, current track, elapsed
 /// time in current track, &c)
 #[derive(Debug)]
@@ -109,7 +55,7 @@ impl PlayState {
     pub async fn new(
         client: &mut Client,
         played_thresh: f64,
-    ) -> std::result::Result<PlayState, crate::clients::Error> {
+    ) -> std::result::Result<PlayState, Error> {
         Ok(PlayState {
             last_server_stat: client.status().await?,
             have_incr_play_count: false,
@@ -126,10 +72,10 @@ impl PlayState {
     /// Poll the server-- update our status; maybe increment the current track's play count; the
     /// caller must arrange to have this method invoked periodically to keep our state fresh
     pub async fn update(&mut self, client: &mut Client) -> Result<()> {
-        let new_stat = client.status().await.map_err(|err| Error::Client {
-            source: err,
-            back: Backtrace::new(),
-        })?;
+        let new_stat = client
+            .status()
+            .await
+            .context("Failed to get client status")?;
 
         match (&self.last_server_stat, &new_stat) {
             (PlayerStatus::Play(last), PlayerStatus::Play(curr))
@@ -176,8 +122,8 @@ impl PlayState {
                         curr.elapsed / curr.duration
                     );
 
-                    let file = curr.file.to_str().ok_or_else(|| Error::BadPath {
-                        pth: curr.file.clone(),
+                    let file = curr.file.to_str().ok_or_else(|| {
+                        anyhow!("Failed to parse path as utf8: `{}`", curr.file.display())
                     })?;
 
                     let curr_pc = play_count::get(client, file).await?.unwrap_or_default();
@@ -189,10 +135,7 @@ impl PlayState {
                         file,
                         SystemTime::now()
                             .duration_since(SystemTime::UNIX_EPOCH)
-                            .map_err(|err| Error::SystemTime {
-                                source: err,
-                                back: Backtrace::new(),
-                            })?
+                            .context("Failed to get system time")?
                             .as_secs(),
                     )
                     .await?;
@@ -213,8 +156,8 @@ impl PlayState {
                         last.elapsed / last.duration
                     );
 
-                    let file = last.file.to_str().ok_or_else(|| Error::BadPath {
-                        pth: last.file.clone(),
+                    let file = last.file.to_str().ok_or_else(|| {
+                        anyhow!("Failed to parse path as utf8: `{}`", last.file.display())
                     })?;
 
                     let skip_count = skipped::get(client, file).await?.unwrap_or_default();
@@ -350,7 +293,10 @@ OK
                 ),
                 "OK\n",
             ),
-            ("sticker set song \"E/Enya - Wild Child.mp3\" unwoundstack.com:playcount 12", "OK\n"),
+            (
+                "sticker set song \"E/Enya - Wild Child.mp3\" unwoundstack.com:playcount 12",
+                "OK\n",
+            ),
         ]));
 
         let mut cli = Client::new(mock).unwrap();
diff --git a/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs b/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
index d64f17c1..24d8dcb5 100644
--- a/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/storage/mod.rs
@@ -1,54 +1,11 @@
-use std::path::PathBuf;
-
-use backtrace::Backtrace;
-
-#[derive(Debug)]
-pub enum Error {
-    PlayerStopped,
-    BadPath {
-        pth: PathBuf,
-    },
-    SystemTime {
-        source: std::time::SystemTimeError,
-        back: Backtrace,
-    },
-    Client {
-        source: crate::clients::Error,
-        back: Backtrace,
-    },
-}
-
-impl std::fmt::Display for Error {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        match self {
-            Error::PlayerStopped => write!(f, "The MPD player is stopped"),
-            Error::BadPath { pth } => write!(f, "Bad path: {:?}", pth),
-            Error::SystemTime { source, back: _ } => {
-                write!(f, "Couldn't get system time: {}", source)
-            }
-            Error::Client { source, back: _ } => write!(f, "Client error: {}", source),
-        }
-    }
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match &self {
-            Error::SystemTime { source, back: _ } => Some(source),
-            Error::Client { source, back: _ } => Some(source),
-            _ => None,
-        }
-    }
-}
-
-type Result<T> = std::result::Result<T, Error>;
+use anyhow::{Error, Result};
 
 pub mod play_count {
-    use backtrace::Backtrace;
+    use anyhow::Context;
 
     use crate::clients::Client;
 
-    use super::{Error, Result};
+    use super::Result;
 
     pub const STICKER: &str = "unwoundstack.com:playcount";
 
@@ -57,10 +14,8 @@ pub mod play_count {
         match client
             .get_sticker::<usize>(file, STICKER)
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })? {
+            .context("Failed to get sticker from client")?
+        {
             Some(n) => Ok(Some(n)),
             None => Ok(None),
         }
@@ -71,10 +26,7 @@ pub mod play_count {
         client
             .set_sticker(file, STICKER, &format!("{}", play_count))
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?;
+            .context("Failed to set_sticker on client")?;
 
         Ok(())
     }
@@ -109,11 +61,11 @@ pub mod play_count {
 }
 
 pub mod skipped {
-    use backtrace::Backtrace;
+    use anyhow::Context;
 
     use crate::clients::Client;
 
-    use super::{Error, Result};
+    use super::Result;
 
     const STICKER: &str = "unwoundstack.com:skipped_count";
 
@@ -122,10 +74,8 @@ pub mod skipped {
         match client
             .get_sticker::<usize>(file, STICKER)
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })? {
+            .context("Failed to get_sticker on client")?
+        {
             Some(n) => Ok(Some(n)),
             None => Ok(None),
         }
@@ -136,19 +86,16 @@ pub mod skipped {
         client
             .set_sticker(file, STICKER, &format!("{}", skip_count))
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })
+            .context("Failed to set_sticker on client")
     }
 }
 
 pub mod last_played {
-    use backtrace::Backtrace;
+    use anyhow::Context;
 
     use crate::clients::Client;
 
-    use super::{Error, Result};
+    use super::Result;
 
     pub const STICKER: &str = "unwoundstack.com:lastplayed";
 
@@ -157,10 +104,7 @@ pub mod last_played {
         client
             .get_sticker::<u64>(file, STICKER)
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })
+            .context("Falied to get_sticker on client")
     }
 
     /// Set the last played for a track
@@ -168,20 +112,17 @@ pub mod last_played {
         client
             .set_sticker(file, STICKER, &format!("{}", last_played))
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?;
+            .context("Failed to set_sticker on client")?;
         Ok(())
     }
 }
 
 pub mod rating_count {
-    use backtrace::Backtrace;
+    use anyhow::Context;
 
     use crate::clients::Client;
 
-    use super::{Error, Result};
+    use super::Result;
 
     pub const STICKER: &str = "unwoundstack.com:ratings_count";
 
@@ -190,10 +131,7 @@ pub mod rating_count {
         client
             .get_sticker::<i8>(file, STICKER)
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })
+            .context("Failed to get_sticker on client")
     }
 
     /// Set the rating count for a track
@@ -201,10 +139,7 @@ pub mod rating_count {
         client
             .set_sticker(file, STICKER, &format!("{}", rating_count))
             .await
-            .map_err(|err| Error::Client {
-                source: err,
-                back: Backtrace::new(),
-            })?;
+            .context("Failed to set_sticker on client")?;
         Ok(())
     }
 }
diff --git a/pkgs/by-name/mp/mpdpopm/src/vars.rs b/pkgs/by-name/mp/mpdpopm/src/vars.rs
index 29b9610d..7cacec66 100644
--- a/pkgs/by-name/mp/mpdpopm/src/vars.rs
+++ b/pkgs/by-name/mp/mpdpopm/src/vars.rs
@@ -2,4 +2,3 @@ pub static VERSION: &str = env!("CARGO_PKG_VERSION");
 pub static AUTHOR: &str = env!("CARGO_PKG_AUTHORS");
 pub static LOCALSTATEDIR: &str = "/home/soispha/.local/state";
 pub static PREFIX: &str = "/home/soispha/.local/share/mpdpopm";
-