diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index 281c4142365c706e7ad4dc9b3581354ce81090cd..0e8227921e27de580476181eb00bafc279c4e242 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -16,6 +16,7 @@
 - cta/CTA#547 - Fix repack backpressure mechanism not requeueing repack requests
 - cta/CTA#562 - CTA Frontend should reject files with owner_uid=0
 - cta/CTA#572 - Do not set the 'queueTrimRequired' flag as true when 'doCleanup' is required
+- cta/CTA#584 - Refactors exception::Errnum class
 
 ### Continuous Integration
 - cta/CTA#278 - Updating system tests for new EOS evict command
diff --git a/common/exception/Errnum.cpp b/common/exception/Errnum.cpp
index 62134701eac67a07528e48a894c4d3dcf0a86e5d..89f105682af054cd8991e9ab323fcb3258c78410 100644
--- a/common/exception/Errnum.cpp
+++ b/common/exception/Errnum.cpp
@@ -18,54 +18,33 @@
 #include "common/exception/Errnum.hpp"
 #include "common/utils/utils.hpp"
 
-#include <errno.h>
-#include <string.h>
+namespace cta::exception {
 
-using namespace cta::exception;
-
-Errnum::Errnum(std::string what):Exception("") {
-  m_errnum = errno;
-  ErrnumConstructorBottomHalf(what);
+Errnum::Errnum(int err, std::string_view what) : Exception(),
+  m_errnum(err),
+  m_strerror(utils::errnoToString(err)) {
+  std::stringstream whatWithErrnum;
+  whatWithErrnum << what << (what.empty() ? "" : " ")
+                 << "Errno=" << m_errnum << ": " << m_strerror;
+  getMessage() << whatWithErrnum.str();
 }
 
-Errnum::Errnum(int err, std::string what):Exception("") {
-  m_errnum = err;
-  ErrnumConstructorBottomHalf(what);
-}
+Errnum::Errnum(std::string_view what) : Errnum(errno, what) { }
 
-void Errnum::ErrnumConstructorBottomHalf(const std::string & what) {
-  m_strerror = utils::errnoToString(m_errnum);
-  std::stringstream w2;
-  if (what.size())
-    w2 << what << " ";
-  w2 << "Errno=" << m_errnum << ": " << m_strerror;
-  getMessage() << w2.str();
+void Errnum::throwOnReturnedErrno(int err, std::string_view context) {
+  if(err) throw Errnum(err, context);
 }
 
-void Errnum::throwOnReturnedErrno (const int err, const std::string &context) {
-  if (err) throw Errnum(err, context);
+void Errnum::throwOnNonZero(int status, std::string_view context) {
+  if(status) throw Errnum(context);
 }
 
-void Errnum::throwOnNonZero(const int status, const std::string &context) {
-  if (status) throw Errnum(context);
+void Errnum::throwOnNull(const void* const ptr, std::string_view context) {
+  if(nullptr == ptr) throw Errnum(context);
 }
 
-void Errnum::throwOnZero(const int status, const std::string &context) {
-  if (!status) throw Errnum(context);
+void Errnum::throwOnMinusOne(ssize_t ret, std::string_view context) {
+  if(ret == -1) throw Errnum(context);
 }
 
-void Errnum::throwOnNull(const void *const f, const std::string &context) {
-  if (nullptr == f) throw Errnum(context);
-}
-
-void Errnum::throwOnNegative(const int ret, const std::string &context) {
-  if (ret < 0) throw Errnum(context);
-}
-
-void Errnum::throwOnMinusOne(const int ret, const std::string &context) {
-  if (-1 == ret) throw Errnum(context);
-}
-
-void Errnum::throwOnNegativeErrnoIfNegative(const int ret, const std::string& context) {
-  if (ret < 0) throw Errnum(-ret, context);
-}
+} // namespace cta::exception
diff --git a/common/exception/Errnum.hpp b/common/exception/Errnum.hpp
index 3f94ec4794dcba337c617a47b3af68307c137806..16bffe5b2a198cbb35f8292b69ddccd7e1d05440 100644
--- a/common/exception/Errnum.hpp
+++ b/common/exception/Errnum.hpp
@@ -17,42 +17,26 @@
 
 #pragma once
 
-#include "Exception.hpp"
-#include <functional>
-#include <system_error>
+#include "common/exception/Exception.hpp"
 
 namespace cta::exception {
 
-  class Errnum: public cta::exception::Exception {
-  public:
-    explicit Errnum(std::string what = "");
-    Errnum(int err, std::string what = "");
-    virtual ~Errnum() = default;
-    int errorNumber() const { return m_errnum; }
-    std::string strError() const { return m_strerror; }
-    static void throwOnReturnedErrno(const int err, const std::string &context = "");
-    template <typename F>
-    static void throwOnReturnedErrnoOrThrownStdException (F f, const std::string &context = "") {
-      try {
-        throwOnReturnedErrno(f(), context);
-      } catch (Errnum &) {
-        throw; // Let the exception of throwOnReturnedErrno pass through.
-      } catch (std::error_code & ec) {
-        throw Errnum(ec.value(), context + " Got an std::error_code: " + ec.message());
-      } catch (std::exception & ex) {
-        throw Exception(context + " Got a standard exception: " + ex.what());
-      }
-    }
-    static void throwOnNonZero(const int status, const std::string &context = "");
-    static void throwOnZero(const int status, const std::string &context = "");
-    static void throwOnNull(const void *const f, const std::string &context = "");
-    static void throwOnNegative(const int ret, const std::string &context = "");
-    static void throwOnMinusOne(const int ret, const std::string &context = "");
-    static void throwOnNegativeErrnoIfNegative(const int ret, const std::string &context = "");
-  protected:
-    void ErrnumConstructorBottomHalf(const std::string & what);
-    int m_errnum;
-    std::string m_strerror;
-  };
+class Errnum : public Exception {
+public:
+  explicit Errnum(std::string_view what = "");
+  Errnum(int err, std::string_view what = "");
+  virtual ~Errnum() = default;
+
+  int errorNumber() const { return m_errnum; }
+
+  static void throwOnReturnedErrno(int err, std::string_view context = "");
+  static void throwOnNonZero(int status, std::string_view context = "");
+  static void throwOnNull(const void *const f, std::string_view context = "");
+  static void throwOnMinusOne(ssize_t ret, const std::string_view context = "");
+
+private:
+  int m_errnum;
+  std::string m_strerror;
+};
 
 } // namespace cta::exception
diff --git a/common/exception/ExceptionTest.cpp b/common/exception/ExceptionTest.cpp
index bf209f8449a751d56c0a48810de8d0b71a0eb700..c7fe6cfea95a8df54a6f0c1fbf7a0be1ae5d16b8 100644
--- a/common/exception/ExceptionTest.cpp
+++ b/common/exception/ExceptionTest.cpp
@@ -83,46 +83,21 @@ namespace unitTests {
   TEST(cta_exceptions, Errnum_throwers) {
     /* throwOnReturnedErrno */
     ASSERT_NO_THROW(cta::exception::Errnum::throwOnReturnedErrno(0, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnReturnedErrno(ENOSPC, "Context"),
-      cta::exception::Errnum);
-
-    ASSERT_NO_THROW(cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([](){ return 0; }, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([](){ return ENOSPC; }, "Context"),
-      cta::exception::Errnum);
-    ASSERT_THROW(cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([](){ throw std::error_code(ENOSPC, std::system_category()); return 0; }, "Context"),
-      cta::exception::Errnum);
-    ASSERT_THROW(cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([](){ throw std::exception(); return 0; }, "Context"),
-      cta::exception::Exception);
-
+    ASSERT_THROW(cta::exception::Errnum::throwOnReturnedErrno(ENOSPC, "Context"), cta::exception::Errnum);
 
     /* throwOnNonZero */
     errno = ENOENT;
     ASSERT_NO_THROW(cta::exception::Errnum::throwOnNonZero(0, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnNonZero(-1, "Context"),
-      cta::exception::Errnum);
+    ASSERT_THROW(cta::exception::Errnum::throwOnNonZero(-1, "Context"), cta::exception::Errnum);
 
     /* throwOnMinusOne */
     errno = ENOENT;
     ASSERT_NO_THROW(cta::exception::Errnum::throwOnMinusOne(0, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnMinusOne(-1, "Context"),
-      cta::exception::Errnum);
-
-    /* throwOnNegative */
-    errno = ENOENT;
-    ASSERT_NO_THROW(cta::exception::Errnum::throwOnNegative(0, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnNegative(-1, "Context"),
-      cta::exception::Errnum);
+    ASSERT_THROW(cta::exception::Errnum::throwOnMinusOne(-1, "Context"), cta::exception::Errnum);
 
     /* throwOnNull */
     errno = ENOENT;
     ASSERT_NO_THROW(cta::exception::Errnum::throwOnNull(this, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnNull(nullptr, "Context"),
-      cta::exception::Errnum);
-
-    /* throwOnZero */
-    errno = ENOENT;
-    ASSERT_NO_THROW(cta::exception::Errnum::throwOnZero(1, "Context"));
-    ASSERT_THROW(cta::exception::Errnum::throwOnZero(0, "Context"),
-      cta::exception::Errnum);
+    ASSERT_THROW(cta::exception::Errnum::throwOnNull(nullptr, "Context"), cta::exception::Errnum);
   }
 }
diff --git a/common/exception/XrootCl.cpp b/common/exception/XrootCl.cpp
index 52b79c3a904bae1a31912d263114553b4ecd08a7..dad93074df532f52e4873b3a12db94c522d5619a 100644
--- a/common/exception/XrootCl.cpp
+++ b/common/exception/XrootCl.cpp
@@ -22,8 +22,8 @@
 
 namespace cta::exception {
 
-XrootCl::XrootCl(const XrdCl::XRootDStatus& status, const std::string & what) {
-  if (!what.empty()) {
+XrootCl::XrootCl(const XrdCl::XRootDStatus& status, std::string_view what) {
+  if(!what.empty()) {
     getMessage() << what << " ";
   }
   getMessage() << status.ToStr().c_str();
@@ -32,8 +32,8 @@ XrootCl::XrootCl(const XrdCl::XRootDStatus& status, const std::string & what) {
                << " status:" << status.status;
 }
 
-void XrootCl::throwOnError(const XrdCl::XRootDStatus& status, const std::string& context) {
-  if (!status.IsOK()) {
+void XrootCl::throwOnError(const XrdCl::XRootDStatus& status, std::string_view context) {
+  if(!status.IsOK()) {
     throw XrootCl(status, context);
   }
 }
diff --git a/common/exception/XrootCl.hpp b/common/exception/XrootCl.hpp
index 57b42807e1270b656eaa6f47f9ce6017156bd1e5..02f7c8da4789b0211a9b63da03ebd25947a22afb 100644
--- a/common/exception/XrootCl.hpp
+++ b/common/exception/XrootCl.hpp
@@ -17,25 +17,23 @@
 
 #pragma once
 
-#include <string>
-
-#include "Exception.hpp"
-
 #include <xrootd/XrdCl/XrdClXRootDResponses.hh>
+#include "common/exception/Exception.hpp"
 
 namespace cta::exception {
 
 /**
- * A class turning the XrootCl (xroot 4 object client) error codes
- * into castor exceptions.
+ * A class turning the XrootCl (xroot 4 object client) error codes into CTA exceptions
  */
-class XrootCl : public cta::exception::Exception {
- public:
-  XrootCl(const XrdCl::XRootDStatus & status, const std::string & context);
+class XrootCl : public Exception {
+public:
+  XrootCl(const XrdCl::XRootDStatus& status, std::string_view context);
   virtual ~XrootCl() = default;
   const XrdCl::XRootDStatus & xRootDStatus() const { return m_status; }
-  static void throwOnError(const XrdCl::XRootDStatus & status, const std::string& context = "");
- protected:
+  static void throwOnError(const XrdCl::XRootDStatus& status, std::string_view context = "");
+
+private:
   XrdCl::XRootDStatus m_status;
 };
+
 } // namespace cta::exception
diff --git a/common/threading/Daemon.cpp b/common/threading/Daemon.cpp
index 4182133502e1f9c16bcb93bb1ff7f93b510932fd..3973b96e96a17d48ad398e3b140d8ba958ecd4e4 100644
--- a/common/threading/Daemon.cpp
+++ b/common/threading/Daemon.cpp
@@ -69,7 +69,7 @@ void cta::server::Daemon::daemonizeIfNotRunInForegroundAndSetUserAndGroup(const
 
     {
       pid_t pid = 0;
-      cta::exception::Errnum::throwOnNegative(pid = fork(),
+      cta::exception::Errnum::throwOnMinusOne(pid = fork(),
         "Failed to daemonize: Failed to fork");
       // If we got a good PID, then we can exit the parent process
       if (0 < pid) {
@@ -85,7 +85,7 @@ void cta::server::Daemon::daemonizeIfNotRunInForegroundAndSetUserAndGroup(const
     umask(0177);
 
     // Run the daemon in a new session
-    cta::exception::Errnum::throwOnNegative(setsid(),
+    cta::exception::Errnum::throwOnMinusOne(setsid(),
       "Failed to daemonize: Failed to run daemon is a new session");
 
     // Redirect standard files to /dev/null
diff --git a/common/utils/utils.cpp b/common/utils/utils.cpp
index 90b5e1d7abb3788acfc494511359c7d84596d4bb..ec4d0241caa330c516d908688c7fd3b9207c2ca3 100644
--- a/common/utils/utils.cpp
+++ b/common/utils/utils.cpp
@@ -21,6 +21,7 @@
 #include "common/utils/strerror_r_wrapper.hpp"
 #include "common/utils/utils.hpp"
 
+#include <algorithm>
 #include <attr/xattr.h>
 #include <limits>
 #include <memory>
diff --git a/disk/DiskFileImplementations.hpp b/disk/DiskFileImplementations.hpp
index d230397affdd83bc9dbb5dbe261ed8efa99edd9e..d5d0f03fcac6f7eb9e636e59ab9dd33d65352034 100644
--- a/disk/DiskFileImplementations.hpp
+++ b/disk/DiskFileImplementations.hpp
@@ -27,7 +27,6 @@
 #include <radosstriper/libradosstriper.hpp>
 
 namespace cta::disk {
-
     /**
      * Namespace managing the reading and writing of files to and from disk.
      */
@@ -60,15 +59,6 @@ namespace cta::disk {
         bool m_closeTried;
       };
       
-      //==============================================================================
-      // CRYPTOPP SIGNER
-      //==============================================================================
-      struct CryptoPPSigner {
-        static std::string sign(const std::string msg, 
-          const CryptoPP::RSA::PrivateKey  & privateKey);
-        static cta::threading::Mutex s_mutex;
-      };
-      
       //==============================================================================
       // XROOT FILES
       //==============================================================================  
@@ -248,5 +238,4 @@ namespace cta::disk {
 	std::string m_truncatedDirectoryURL; // root://.../ part of the path is removed
 	const uint16_t c_xrootTimeout = 15; 
       };
-
 } // namespace cta::disk
diff --git a/objectstore/BackendRados.cpp b/objectstore/BackendRados.cpp
index 52441bb1d7599c9f90277d09341ab5c6e471b41d..d3086938f18d0e17ad20e05a465fb7a9a6ac2d8d 100644
--- a/objectstore/BackendRados.cpp
+++ b/objectstore/BackendRados.cpp
@@ -90,22 +90,22 @@ BackendRados::BackendRados(log::Logger& logger, const std::string& userId, const
   m_logger(logger), m_user(userId), m_pool(pool), m_namespace(radosNameSpace)
 {
   log::LogContext lc(logger);
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+  throwOnReturnedErrnoOrThrownStdException(
     [this, &userId]() { return -m_cluster.init(userId.c_str()); },
     "In BackendRados::BackendRados, failed to m_cluster.init");
   try {
     RadosTimeoutLogger rtl;
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+    throwOnReturnedErrnoOrThrownStdException(
       [this]() { return -m_cluster.conf_read_file(nullptr); },
       "In BackendRados::BackendRados, failed to m_cluster.conf_read_file");
     rtl.logIfNeeded("In BackendRados::BackendRados(): m_cluster.conf_read_file()", "no object");
     rtl.reset();
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+    throwOnReturnedErrnoOrThrownStdException(
       [this]() { return -m_cluster.conf_parse_env(nullptr);},
       "In BackendRados::BackendRados, failed to m_cluster.conf_parse_env");
     rtl.logIfNeeded("In BackendRados::BackendRados(): m_cluster.conf_parse_env()", "no object");
     rtl.reset();
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+    throwOnReturnedErrnoOrThrownStdException(
       [this]() { return -m_cluster.connect();},
       "In BackendRados::BackendRados, failed to m_cluster.connect");
     rtl.logIfNeeded("In BackendRados::BackendRados(): m_cluster.connect()", "no object");
@@ -116,7 +116,7 @@ BackendRados::BackendRados(log::Logger& logger, const std::string& userId, const
       params.add("contextId", i);
       lc.log(log::DEBUG, "BackendRados::BackendRados() about to create a new context");
       rtl.reset();
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+      throwOnReturnedErrnoOrThrownStdException(
         [this, &pool]() { return -m_cluster.ioctx_create(pool.c_str(), m_radosCtxPool.back()); },
         "In BackendRados::BackendRados, failed to m_cluster.ioctx_create");
       rtl.logIfNeeded("In BackendRados::BackendRados(): m_cluster.ioctx_create()", "no object");
@@ -130,7 +130,7 @@ BackendRados::BackendRados(log::Logger& logger, const std::string& userId, const
 lc.log(log::DEBUG, "BackendRados::BackendRados() namespace set. About to test access.");
       // Try to read a non-existing object through the newly created context, in hope this will protect against
       // race conditions(?) when creating the contexts in a tight loop.
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+      throwOnReturnedErrnoOrThrownStdException(
         [this, &bl, &rtl]() {
           auto rc = m_radosCtxPool.back().read("TestObjectThatDoesNotNeedToExist", bl, 1, 0);
           rtl.logIfNeeded("In BackendRados::BackendRados(): m_radosCtxPool.back().read()", "TestObjectThatDoesNotNeedToExist");
@@ -215,7 +215,7 @@ void BackendRados::create(const std::string& name, const std::string& content) {
   {
     RadosTimeoutLogger rtl;
     try {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+      throwOnReturnedErrnoOrThrownStdException(
         [this, &name, &wop]() {
           return -getRadosCtx().operate(name, &wop);
         },
@@ -261,7 +261,7 @@ void BackendRados::atomicOverwrite(const std::string& name, const std::string& c
   bl.append(content.c_str(), content.size());
   wop.write_full(bl);
   RadosTimeoutLogger rtl;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+  throwOnReturnedErrnoOrThrownStdException(
     [this, &name, &wop]() { return -getRadosCtx().operate(name, &wop); },
     std::string("In BackendRados::atomicOverwrite, failed to assert existence or write: ") + name);
   rtl.logIfNeeded("In BackendRados::atomicOverwrite(): m_radosCtx.operate(assert_exists+write_full)", name);
@@ -272,7 +272,7 @@ std::string BackendRados::read(const std::string& name) {
   librados::bufferlist bl;
   RadosTimeoutLogger rtl;
   try {
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+    throwOnReturnedErrnoOrThrownStdException(
     [this, &name, &bl]() {
       auto rc = getRadosCtx().read(name, bl, std::numeric_limits<int32_t>::max(), 0);
       return rc < 0 ? rc : 0;
@@ -295,7 +295,7 @@ std::string BackendRados::read(const std::string& name) {
 
 void BackendRados::remove(const std::string& name) {
   RadosTimeoutLogger rtl;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+  throwOnReturnedErrnoOrThrownStdException(
     [this, &name]() { return -getRadosCtx().remove(name); });
   rtl.logIfNeeded("In BackendRados::remove(): m_radosCtx.remove()", name);
 }
@@ -305,7 +305,7 @@ bool BackendRados::exists(const std::string& name) {
   time_t date;
   RadosTimeoutLogger rtl;
   int statRet;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+  throwOnReturnedErrnoOrThrownStdException(
     [this, &statRet, &name, &size, &date]() {
       statRet = getRadosCtx().stat(name, &size, &date);
       return 0;
@@ -327,19 +327,19 @@ std::list<std::string> BackendRados::list() {
   std::list<std::string> ret;
   auto& ctx = getRadosCtx();
   decltype(ctx.nobjects_begin()) o;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+  throwOnReturnedErrnoOrThrownStdException(
     [&o, &ctx]() {
       o = ctx.nobjects_begin();
       return 0;
     }, "In BackendRados::list(): failed to ctx.nobjects_begin()");
   bool go;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+  throwOnReturnedErrnoOrThrownStdException(
     [&go, &o, &ctx]() {
       go = (o != ctx.nobjects_end());
       return 0;
     }, "In BackendRados::list(): failed ctx.nobjects_end()");
   while(go) {
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException(
+    throwOnReturnedErrnoOrThrownStdException(
       [&ret, &o, &go, &ctx]() {
         ret.push_back(o->get_oid());
         o++;
@@ -367,7 +367,7 @@ void BackendRados::ScopedLock::releaseNotify() {
   // we hence overlook the ENOENT errors.
   TIMESTAMPEDPRINT("Pre-release");
   RadosTimeoutLogger rtl1;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &rtl1]() {
+  throwOnReturnedErrnoOrThrownStdException([this, &rtl1]() {
       int rc=m_context.unlock(m_oid, "lock", m_clientId);
       rtl1.logIfNeeded("In BackendRados::ScopedLock::releaseNotify(): m_context.unlock()", m_oid);
       return rc==-ENOENT?0:-rc;
@@ -381,7 +381,7 @@ void BackendRados::ScopedLock::releaseNotify() {
   // We use a fire and forget aio call.
   librados::AioCompletion * completion = librados::Rados::aio_create_completion(nullptr, nullptr, nullptr);
   RadosTimeoutLogger rtl2;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &completion, &bl]() { return -m_context.aio_notify(m_oid, completion, bl, 10000, nullptr);},
+  throwOnReturnedErrnoOrThrownStdException([this, &completion, &bl]() { return -m_context.aio_notify(m_oid, completion, bl, 10000, nullptr);},
       "In BackendRados::ScopedLock::releaseNotify(): failed to aio_notify()");
   rtl2.logIfNeeded("In BackendRados::ScopedLock::releaseNotify(): m_context.aio_notify()", m_oid);
   completion->release();
@@ -397,7 +397,7 @@ void BackendRados::ScopedLock::releaseBackoff() {
   TIMESTAMPEDPRINT("Pre-release");
   RadosTimeoutLogger rtl1;
   int rc;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &rc]() {
+  throwOnReturnedErrnoOrThrownStdException([this, &rc]() {
     rc=m_context.unlock(m_oid, "lock", m_clientId);
     return 0;
   }, "In BackendRados::ScopedLock::releaseBackoff(): failed m_context.unlock()");
@@ -437,7 +437,7 @@ BackendRados::LockWatcher::LockWatcher(librados::IoCtx& context, const std::stri
   m_internal->m_future = m_internal->m_promise.get_future();
   TIMESTAMPEDPRINT("Pre-watch2");
   RadosTimeoutLogger rtl;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &name]() { return -m_context.watch2(name, &m_watchHandle, m_internal.get());},
+  throwOnReturnedErrnoOrThrownStdException([this, &name]() { return -m_context.watch2(name, &m_watchHandle, m_internal.get());},
     "In BackendRados::LockWatcher::LockWatcher(): failed m_context.watch2()");
   rtl.logIfNeeded("In BackendRados::LockWatcher::LockWatcher(): m_context.watch2()", name);
   TIMESTAMPEDPRINT("Post-watch2");
@@ -472,7 +472,7 @@ BackendRados::LockWatcher::~LockWatcher() {
   librados::AioCompletion *completion = librados::Rados::aio_create_completion(m_internal.release(), nullptr, Internal::deleter);
   RadosTimeoutLogger rtl;
   try {
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &completion]() {
+    throwOnReturnedErrnoOrThrownStdException([this, &completion]() {
       m_context.aio_unwatch(m_watchHandle, completion);
       return 0;
     }, "In BackendRados::LockWatcher::~LockWatcher(): failed m_context.aio_unwatch()");
@@ -526,13 +526,13 @@ void BackendRados::lockNotify(const std::string& name, uint64_t timeout_us, Lock
     TIMESTAMPEDPRINT(lockType==LockType::Shared?"Pre-lock (shared)":"Pre-lock (exclusive)");
     RadosTimeoutLogger rtl;
     if(lockType==LockType::Shared) {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
         rc = radosCtx.lock_shared(name, "lock", clientId, "", "", &tv, 0);
         return 0;
       }, "In BackendRados::lockNotify: failed radosCtx.lock_shared()");
       rtl.logIfNeeded("In BackendRados::lockNotify(): m_radosCtx.lock_shared()", name);
     } else {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
         rc = radosCtx.lock_exclusive(name, "lock", clientId, "", &tv, 0);
         return 0;
       }, "In BackendRados::lockNotify: failed radosCtx.lock_exclusive()");
@@ -553,13 +553,13 @@ void BackendRados::lockNotify(const std::string& name, uint64_t timeout_us, Lock
     // We need to retry the lock after establishing the watch: it could have been released during that time.
     rtl.reset();
     if (lockType==LockType::Shared) {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
         rc = radosCtx.lock_shared(name, "lock", clientId, "", "", &tv, 0);
         return 0;
       }, "In BackendRados::lockNotify: failed radosCtx.lock_shared()(2)");
       rtl.logIfNeeded("In BackendRados::lockNotify(): m_radosCtx.lock_shared()", name);
     } else {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &tv]() {
         rc = radosCtx.lock_exclusive(name, "lock", clientId, "", &tv, 0);
         return 0;
       }, "In BackendRados::lockNotify: failed radosCtx.lock_shared()(2)");
@@ -595,12 +595,12 @@ void BackendRados::lockNotify(const std::string& name, uint64_t timeout_us, Lock
   // Get the size:
   uint64_t size;
   time_t date;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name, &size, &date]() { return -radosCtx.stat(name, &size, &date); },
+  throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name, &size, &date]() { return -radosCtx.stat(name, &size, &date); },
       std::string("In BackendRados::lock, failed to librados::IoCtx::stat: ") +
       name + "/" + "lock" + "/" + clientId + "//");
   if (!size) {
     // The object has a zero size: we probably created it by attempting the locking.
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name]() { return -radosCtx.remove(name);},
+    throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name]() { return -radosCtx.remove(name);},
         std::string("In BackendRados::lock, failed to librados::IoCtx::remove: ") +
         name + "//");
     throw cta::exception::NoSuchObject(std::string("In BackendRados::lockWatch(): "
@@ -749,14 +749,14 @@ void BackendRados::lockBackoff(const std::string& name, uint64_t timeout_us, Loc
     TIMESTAMPEDPRINT(lockType==LockType::Shared?"Pre-lock (shared)":"Pre-lock (exclusive)");
     RadosTimeoutLogger rtl;
     if (lockType==LockType::Shared) {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &radosLockExpirationTime, &timingMeasurements, &t, &timeoutTimer]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &radosLockExpirationTime, &timingMeasurements, &t, &timeoutTimer]() {
         rc = radosCtx.lock_shared(name, "lock", clientId, "", "", &radosLockExpirationTime, 0);
         timingMeasurements.addSuccess(t.secs(), timeoutTimer.secs());
         return 0;
       }, "In BackendRados::lockBackoff(): failed radosCtx.lock_shared()");
       rtl.logIfNeeded("In BackendRados::lockBackoff(): radosCtx.lock_shared()", name);
     } else {
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &radosLockExpirationTime, &timingMeasurements, &t, &timeoutTimer]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &radosCtx, &name, &clientId, &radosLockExpirationTime, &timingMeasurements, &t, &timeoutTimer]() {
         rc = radosCtx.lock_exclusive(name, "lock", clientId, "", &radosLockExpirationTime, 0);
         timingMeasurements.addSuccess(t.secs(), timeoutTimer.secs());
         return 0;
@@ -800,12 +800,12 @@ void BackendRados::lockBackoff(const std::string& name, uint64_t timeout_us, Loc
   // Get the size:
   uint64_t size;
   time_t date;
-  cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name, &size, &date]() { return -radosCtx.stat(name, &size, &date); },
+  throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name, &size, &date]() { return -radosCtx.stat(name, &size, &date); },
       std::string("In BackendRados::lockBackoff, failed to librados::IoCtx::stat: ") +
       name + "/" + "lock" + "/" + clientId + "//");
   if (!size) {
     // The object has a zero size: we probably created it by attempting the locking.
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name]() { return -radosCtx.remove(name); },
+    throwOnReturnedErrnoOrThrownStdException ([&radosCtx, &name]() { return -radosCtx.remove(name); },
         std::string("In BackendRados::lockBackoff, failed to librados::IoCtx::remove: ") +
         name + "//");
     throw cta::exception::NoSuchObject(std::string("In BackendRados::lockBackoff(): "
@@ -871,7 +871,7 @@ m_backend(be), m_name(name), m_value(value), m_job(), m_jobFuture(m_job.get_futu
     m_radosTimeoutLogger.reset();
     RadosTimeoutLogger rtl;
     int rc;
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &rc, &aioc, &wop]() {
+    throwOnReturnedErrnoOrThrownStdException([this, &rc, &aioc, &wop]() {
       rc=m_backend.getRadosCtx().aio_operate(m_name, aioc, &wop);
       return 0;
     }, "In BackendRados::AsyncCreator::AsyncCreator(): failed m_backend.getRadosCtx().aio_operate()");
@@ -903,7 +903,7 @@ void BackendRados::AsyncCreator::createExclusiveCallback(librados::completion_t
         RadosTimeoutLogger rtl;
         int rc;
         librados::AioCompletion * aioc = librados::Rados::aio_create_completion(pThis, statCallback, nullptr);
-        cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &ac, &aioc]() {
+        throwOnReturnedErrnoOrThrownStdException([&rc, &ac, &aioc]() {
           rc=ac.m_backend.getRadosCtx().aio_stat(ac.m_name, aioc, &ac.m_size, &ac.m_time);
           return 0;
         }, "In BackendRados::AsyncCreator::createExclusiveCallback(): failed m_backend.getRadosCtx().aio_operate()");
@@ -940,7 +940,7 @@ void BackendRados::AsyncCreator::statCallback(librados::completion_t completion,
         ac.m_radosTimeoutLogger.reset();
         RadosTimeoutLogger rtl;
         int rc;
-        cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &ac, &aioc, &wop]() {
+        throwOnReturnedErrnoOrThrownStdException([&rc, &ac, &aioc, &wop]() {
           rc=ac.m_backend.getRadosCtx().aio_operate(ac.m_name, aioc, &wop);
           return 0;
         }, "In BackendRados::AsyncCreator::statCallback(): failed m_backend.getRadosCtx().aio_operate()");
@@ -976,7 +976,7 @@ void BackendRados::AsyncCreator::statCallback(librados::completion_t completion,
         RadosTimeoutLogger rtl;
         int rc;
         librados::AioCompletion * aioc = librados::Rados::aio_create_completion(pThis, statCallback, nullptr);
-        cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &ac, &aioc]() {
+        throwOnReturnedErrnoOrThrownStdException([&rc, &ac, &aioc]() {
           rc=ac.m_backend.getRadosCtx().aio_stat(ac.m_name, aioc, &ac.m_size, &ac.m_time);
           return 0;
         }, "In BackendRados::AsyncCreator::statCallback(): failed m_backend.getRadosCtx().aio_operate()");
@@ -1019,7 +1019,7 @@ BackendRados::AsyncUpdater::AsyncUpdater(BackendRados& be, const std::string& na
             RadosTimeoutLogger rtl;
             m_radosTimeoutLogger.reset();
             int rc;
-            cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &rc, &aioc]() {
+            throwOnReturnedErrnoOrThrownStdException([this, &rc, &aioc]() {
               rc=m_backend.getRadosCtx().aio_read(m_name, aioc, &m_radosBufferList, std::numeric_limits<int32_t>::max(), 0);
               return 0;
             }, std::string("In AsyncUpdater::AsyncUpdater::lock_lambda(): failed getRadosCtx().aio_read(): ")+m_name);
@@ -1136,7 +1136,7 @@ void BackendRados::AsyncUpdater::UpdateJob::execute() {
       RadosTimeoutLogger rtl;
       au.m_radosTimeoutLogger.reset();
       int rc;
-      cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &au, &aioc]() {
+      throwOnReturnedErrnoOrThrownStdException([&rc, &au, &aioc]() {
         rc=au.m_backend.getRadosCtx().aio_write_full(au.m_name, aioc, au.m_radosBufferList);
         return 0;
       }, "In BackendRados::AsyncUpdater::UpdateJob::execute(): failed m_backend.getRadosCtx().aio_write_full()");
@@ -1169,7 +1169,7 @@ void BackendRados::AsyncUpdater::commitCallback(librados::completion_t completio
     au.m_radosTimeoutLogger.reset();
     RadosTimeoutLogger rtl;
     int rc;
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &au, &aioc]() {
+    throwOnReturnedErrnoOrThrownStdException([&rc, &au, &aioc]() {
       rc=au.m_backend.getRadosCtx().aio_unlock(au.m_name, "lock", au.m_lockClient, aioc);
       return 0;
     }, "In BackendRados::AsyncUpdater::commitCallback(): failed to m_backend.getRadosCtx().aio_unlock()");
@@ -1234,7 +1234,7 @@ BackendRados::AsyncDeleter::AsyncDeleter(BackendRados& be, const std::string& na
             m_radosTimeoutLogger.reset();
             RadosTimeoutLogger rtl;
             int rc;
-            cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([this, &rc, &aioc]() {
+            throwOnReturnedErrnoOrThrownStdException([this, &rc, &aioc]() {
               rc=m_backend.getRadosCtx().aio_remove(m_name, aioc);
               return 0;
             }, "In BackendRados::AsyncDeleter::AsyncDeleter(): failed m_backend.getRadosCtx().aio_remove()");
@@ -1300,7 +1300,7 @@ void BackendRados::AsyncLockfreeFetcher::AioReadPoster::execute() {
     RadosTimeoutLogger rtl;
     au.m_radosTimeoutLogger.reset();
     int rc;
-    cta::exception::Errnum::throwOnReturnedErrnoOrThrownStdException([&rc, &au, &aioc]() {
+    throwOnReturnedErrnoOrThrownStdException([&rc, &au, &aioc]() {
       rc=au.m_backend.getRadosCtx().aio_read(au.m_name, aioc, &au.m_radosBufferList, std::numeric_limits<int32_t>::max(), 0);
       return 0;
     }, "In BackendRados::AsyncLockfreeFetcher::AioReadPoster::execute(): failed m_backend.getRadosCtx().aio_read()");
diff --git a/objectstore/BackendRados.hpp b/objectstore/BackendRados.hpp
index e171d710b1b796b429ebb15e97fa98e143f8f100..71a7865bfe4e07f7bebb258ba05a80e4d459d912 100644
--- a/objectstore/BackendRados.hpp
+++ b/objectstore/BackendRados.hpp
@@ -112,6 +112,21 @@ public:
   static const size_t c_backoffFraction;
   static const uint64_t c_maxWait;
 
+  // This method was originally part of cta::exception::Errnum. As it is only used in the BackupRados class,
+  // it has been moved here.
+  template <typename F>
+  static void throwOnReturnedErrnoOrThrownStdException(F f, std::string_view context = "") {
+    try {
+      exception::Errnum::throwOnReturnedErrno(f(), context);
+    } catch(exception::Errnum&) {
+      throw; // Let the exception of throwOnReturnedErrno pass through
+    } catch(std::error_code& ec) {
+      throw exception::Errnum(ec.value(), std::string(context) + " Got a std::error_code: " + ec.message());
+    } catch(std::exception& ex) {
+      throw exception::Exception(std::string(context) + " Got a std::exception: " + ex.what());
+    }
+  }
+
 private:
   static std::string createUniqueClientId();
   /** This function will lock or die (actually throw, that is) */
diff --git a/objectstore/BackendTest.cpp b/objectstore/BackendTest.cpp
index db47f803da0ae5dff33652cd823eb1974d9a9293..65312c8142a5ebd88e585dc38162f2228d286d86 100644
--- a/objectstore/BackendTest.cpp
+++ b/objectstore/BackendTest.cpp
@@ -222,6 +222,13 @@ TEST_P(BackendAbstractTest, ParametersInterface) {
   //std::cout << params->toStr() << std::endl;
 }
 
+TEST(BackendAbstractTest, Errnum_throwers) {
+  ASSERT_NO_THROW(cta::objectstore::BackendRados::throwOnReturnedErrnoOrThrownStdException([](){ return 0; }, "Context"));
+  ASSERT_THROW(cta::objectstore::BackendRados::throwOnReturnedErrnoOrThrownStdException([](){ return ENOSPC; }, "Context"), cta::exception::Errnum);
+  ASSERT_THROW(cta::objectstore::BackendRados::throwOnReturnedErrnoOrThrownStdException([](){ throw std::error_code(ENOSPC, std::system_category()); return 0; }, "Context"), cta::exception::Errnum);
+  ASSERT_THROW(cta::objectstore::BackendRados::throwOnReturnedErrnoOrThrownStdException([](){ throw std::exception(); return 0; }, "Context"), cta::exception::Exception);
+}
+
 static cta::objectstore::BackendVFS osVFS(__LINE__, __FILE__);
 #ifdef TEST_RADOS
 static cta::log::DummyLogger dl("", "");