diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index 94efb5edd7de0897fea2010e97440751e19b6fa1..0b84b9c426471568d680fe2b219525fc073eca1b 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -64,6 +64,7 @@
 #include "common/exception/FileSizeMismatch.hpp"
 #include "common/exception/TapeFseqMismatch.hpp"
 #include "common/exception/UserError.hpp"
+#include "common/exception/UserErrorWithCacheInfo.hpp"
 #include "common/log/LogContext.hpp"
 #include "common/log/Logger.hpp"
 #include "common/optional.hpp"
@@ -164,6 +165,7 @@ public:
    * used by the Catalogue to determine the mount policy to be used when
    * archiving the file.
    * @return The new archive file identifier.
+   * @throw UserErrorWithCacheInfo if there was a user error.
    */
   virtual uint64_t checkAndGetNextArchiveFileId(
     const std::string &diskInstanceName,
diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index cd94aa12cd9d0b993910c85acae531b1b2416907..22939e944fbe61000fc875207809b65c88f23447 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -21,6 +21,7 @@
 #include "common/Constants.hpp"
 #include "common/exception/Exception.hpp"
 #include "common/exception/UserError.hpp"
+#include "common/exception/UserErrorWithCacheInfo.hpp"
 #include "common/make_unique.hpp"
 #include "common/threading/Thread.hpp"
 #include "common/threading/Mutex.hpp"
@@ -7567,7 +7568,7 @@ TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_no_mount_rules)
   requesterIdentity.name = requesterName;
   requesterIdentity.group = "group";
 
-  ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstance, m_storageClassSingleCopy.name, requesterIdentity), exception::UserError);
+  ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstance, m_storageClassSingleCopy.name, requesterIdentity), exception::UserErrorWithCacheInfo);
 }
 
 TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_after_cached_and_then_deleted_requester_mount_rule) {
@@ -7644,7 +7645,7 @@ TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_after_cached_an
   // Try to get an archive ID which should now fail because there is no user
   // mount rule and the invalidated user mount rule cache should not hide this
   // fact
-  ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserError);
+  ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserErrorWithCacheInfo);
 }
 
 TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_requester_mount_rule) {
@@ -7860,7 +7861,7 @@ TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_after_cached_an
   // Try to get an archive ID which should now fail because there is no group
   // mount rule and the invalidated group mount rule cache should not hide this
   // fact
-  ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserError);
+  ASSERT_THROW(m_catalogue->checkAndGetNextArchiveFileId(diskInstanceName, m_storageClassSingleCopy.name, requesterIdentity), exception::UserErrorWithCacheInfo);
 }
 
 TEST_P(cta_catalogue_CatalogueTest, checkAndGetNextArchiveFileId_requester_mount_rule_overide) {
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 2d61efebb818b38c229287b00b8a487daec42f9c..4db898a66bb1dcd41471ebc6cab0c07c9de163d1 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -560,7 +560,7 @@ common::dataStructures::VirtualOrganization RdbmsCatalogue::getCachedVirtualOrga
       auto conn = m_connPool.getConn();
       return getVirtualOrganizationOfTapepool(conn,tapepoolName);
     };
-    return m_tapepoolVirtualOrganizationCache.getCachedValue(tapepoolName,getNonCachedValue);
+    return m_tapepoolVirtualOrganizationCache.getCachedValue(tapepoolName,getNonCachedValue).value;
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
@@ -5538,7 +5538,7 @@ void RdbmsCatalogue::createRequesterGroupMountRule(
 //------------------------------------------------------------------------------
 // getCachedRequesterGroupMountPolicy
 //------------------------------------------------------------------------------
-optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedRequesterGroupMountPolicy(const Group &group)
+ValueAndTimeBasedCacheInfo<optional<common::dataStructures::MountPolicy> > RdbmsCatalogue::getCachedRequesterGroupMountPolicy(const Group &group)
   const {
   try {
     auto getNonCachedValue = [&] {
@@ -5764,7 +5764,7 @@ bool RdbmsCatalogue::requesterMountRuleExists(rdbms::Conn &conn, const std::stri
 //------------------------------------------------------------------------------
 // getCachedRequesterMountPolicy
 //------------------------------------------------------------------------------
-optional<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedRequesterMountPolicy(const User &user) const {
+ValueAndTimeBasedCacheInfo <optional <common::dataStructures::MountPolicy> > RdbmsCatalogue::getCachedRequesterMountPolicy(const User &user) const {
   try {
     auto getNonCachedValue = [&] {
       auto conn = m_connPool.getConn();
@@ -5992,7 +5992,7 @@ std::list<common::dataStructures::MountPolicy> RdbmsCatalogue::getCachedMountPol
       auto conn = m_connPool.getConn();
       return getMountPolicies(conn);
     };
-    return m_allMountPoliciesCache.getCachedValue("all",getNonCachedValue);
+    return m_allMountPoliciesCache.getCachedValue("all",getNonCachedValue).value;
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
@@ -7375,17 +7375,20 @@ uint64_t RdbmsCatalogue::checkAndGetNextArchiveFileId(const std::string &diskIns
       throw ue;
     }
 
-    auto mountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
+    const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
+    const auto userMountPolicy = userMountPolicyAndCacheInfo.value;
     // Only consider the requester's group if there is no user mount policy
-    if(!mountPolicy) {
-      const auto groupMountPolicy = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
+    if(!userMountPolicy) {
+      const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
+      const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value;
 
       if(!groupMountPolicy) {
 
-        const auto defaultMountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
+        const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
+        const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value;
 
-        if(!defaultMountPolicy) {
-          exception::UserError ue;
+        if(!defaultUserMountPolicy) {
+          exception::UserErrorWithCacheInfo ue(defaultUserMountPolicyAndCacheInfo.cacheInfo);
           ue.getMessage() << "Failed to check and get next archive file ID: No mount rules: storageClass=" <<
             storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group;
           throw ue;
@@ -7399,6 +7402,13 @@ uint64_t RdbmsCatalogue::checkAndGetNextArchiveFileId(const std::string &diskIns
       auto conn = m_connPool.getConn();
       return getNextArchiveFileId(conn);
     }
+  } catch(exception::UserErrorWithCacheInfo &ue) {
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("cacheInfo", ue.cacheInfo)
+       .add("userError", ue.getMessage().str());
+    lc.log(log::INFO, "Catalogue::checkAndGetNextArchiveFileId caught a UserErrorWithCacheInfo");
+    throw;
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
@@ -7433,23 +7443,31 @@ common::dataStructures::ArchiveFileQueueCriteria RdbmsCatalogue::getArchiveFileQ
     }
 
     // Get the mount policy - user mount policies overrule group ones
-    auto mountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
-    if(!mountPolicy) {
-      mountPolicy = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
+    const auto userMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, user.name));
+    const auto userMountPolicy = userMountPolicyAndCacheInfo.value;
 
-      if(!mountPolicy) {
-        mountPolicy = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
+    if(userMountPolicy) {
+      return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *userMountPolicy);
+    } else {
+      const auto groupMountPolicyAndCacheInfo = getCachedRequesterGroupMountPolicy(Group(diskInstanceName, user.group));
+      const auto groupMountPolicy = groupMountPolicyAndCacheInfo.value;
 
-        if(!mountPolicy) {
-          exception::UserError ue;
+      if(groupMountPolicy) {
+        return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *groupMountPolicy);
+      } else {
+        const auto defaultUserMountPolicyAndCacheInfo = getCachedRequesterMountPolicy(User(diskInstanceName, "default"));
+        const auto defaultUserMountPolicy = defaultUserMountPolicyAndCacheInfo.value;
+
+        if(defaultUserMountPolicy) {
+          return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *defaultUserMountPolicy);
+        } else {
+          exception::UserErrorWithCacheInfo ue(defaultUserMountPolicyAndCacheInfo.cacheInfo);
           ue.getMessage() << "Failed to get archive file queue criteria: No mount rules: storageClass=" <<
             storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << user.group;
           throw ue;
         }
       }
     }
-
-    return common::dataStructures::ArchiveFileQueueCriteria(copyToPoolMap, *mountPolicy);
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
@@ -7468,7 +7486,7 @@ common::dataStructures::TapeCopyToPoolMap RdbmsCatalogue::getCachedTapeCopyToPoo
       auto conn = m_connPool.getConn();
       return getTapeCopyToPoolMap(conn, storageClass);
     };
-    return m_tapeCopyToPoolCache.getCachedValue(storageClass, getNonCachedValue);
+    return m_tapeCopyToPoolCache.getCachedValue(storageClass, getNonCachedValue).value;
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
@@ -7523,7 +7541,7 @@ uint64_t RdbmsCatalogue::getCachedExpectedNbArchiveRoutes(const StorageClass &st
       auto conn = m_connPool.getConn();
       return getExpectedNbArchiveRoutes(conn, storageClass);
     };
-    return m_expectedNbArchiveRoutesCache.getCachedValue(storageClass, getNonCachedValue);
+    return m_expectedNbArchiveRoutesCache.getCachedValue(storageClass, getNonCachedValue).value;
   } catch (exception::LostDatabaseConnection &le) {
     throw exception::LostDatabaseConnection(std::string(__FUNCTION__) + " failed: " + le.getMessage().str());
   } catch(exception::UserError &) {
@@ -7793,7 +7811,7 @@ bool RdbmsCatalogue::isCachedAdmin(const common::dataStructures::SecurityIdentit
     auto getNonCachedValue = [&] {
       return isNonCachedAdmin(admin);
     };
-    return m_isAdminCache.getCachedValue(admin, getNonCachedValue);
+    return m_isAdminCache.getCachedValue(admin, getNonCachedValue).value;
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
@@ -8164,7 +8182,7 @@ RdbmsCatalogue::getCachedActivitiesWeights(const std::string& diskInstance) cons
       auto conn = m_connPool.getConn();
       return getActivitiesWeights(conn, diskInstance);
     };
-    return m_activitiesFairShareWeights.getCachedValue(diskInstance, getNonCachedValue);
+    return m_activitiesFairShareWeights.getCachedValue(diskInstance, getNonCachedValue).value;
   } catch(exception::UserError &) {
     throw;
   } catch(exception::Exception &ex) {
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index 0c3bcf6d58ccc782cc33b6e27e18c2721936ac20..9b025b28c92c91850c314857fed9ced35f4bff3b 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -117,6 +117,7 @@ public:
    * used by the Catalogue to determine the mount policy to be used when
    * archiving the file.
    * @return The new archive file identifier.
+   * @throw UserErrorWithTimeBasedCacheInfo if there was a user error.
    */
   uint64_t checkAndGetNextArchiveFileId(
     const std::string &diskInstanceName,
@@ -1230,8 +1231,9 @@ protected:
    * @param user The fully qualified user, in other words the name of the disk
    * instance and the name of the group.
    * @return The mount policy or nullopt if one does not exists.
+   * @throw UserErrorWithTimeBasedCacheInfo if there was a user error.
    */
-  optional<common::dataStructures::MountPolicy> getCachedRequesterMountPolicy(const User &user) const;
+  ValueAndTimeBasedCacheInfo<optional<common::dataStructures::MountPolicy> > getCachedRequesterMountPolicy(const User &user) const;
 
   /**
    * Returns the specified requester mount-policy or nullopt if one does not
@@ -1305,7 +1307,7 @@ protected:
    * instance and the name of the group.
    * @return The cached mount policy or nullopt if one does not exists.
    */
-  optional<common::dataStructures::MountPolicy> getCachedRequesterGroupMountPolicy(const Group &group) const;
+  ValueAndTimeBasedCacheInfo<optional<common::dataStructures::MountPolicy> > getCachedRequesterGroupMountPolicy(const Group &group) const;
 
   /**
    * Returns the specified requester-group mount-policy or nullptr if one does
diff --git a/catalogue/TimeBasedCache.hpp b/catalogue/TimeBasedCache.hpp
index 32064a3539efe0c65aa7efa999026ab1d32ba7f1..437cd38708108e6f69bad5353a4462d42fc6d050 100644
--- a/catalogue/TimeBasedCache.hpp
+++ b/catalogue/TimeBasedCache.hpp
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include "catalogue/ValueAndTimeBasedCacheInfo.hpp"
 #include "common/make_unique.hpp"
 #include "common/threading/Mutex.hpp"
 #include "common/threading/MutexLocker.hpp"
@@ -40,11 +41,11 @@ public:
   }
 
   /**
-   * Get the cached value corresponing to the specified key.
+   * Get the cached value corresponding to the specified key.
    *
    * This method updates the cache when necessary.
    */
-  template<typename Callable> Value getCachedValue(const Key &key, const Callable &getNonCachedValue) {
+  template<typename Callable> ValueAndTimeBasedCacheInfo<Value> getCachedValue(const Key &key, const Callable &getNonCachedValue) {
     const time_t now = time(nullptr);
 
     threading::MutexLocker cacheLock(m_mutex);
@@ -56,16 +57,17 @@ public:
       const time_t ageSecs = now - cachedValue.timestamp;
 
       if (m_maxAgeSecs >= ageSecs) { // Cached value is fresh
-        return cachedValue.value;
+        return ValueAndTimeBasedCacheInfo<Value>(cachedValue.value, "Fresh value found in cache");
       } else { // Cached value is stale
         cachedValue.value = getNonCachedValue();
         cachedValue.timestamp = ::time(nullptr);
-        return cachedValue.value;
+
+        return ValueAndTimeBasedCacheInfo<Value>(cachedValue.value, "Stale value found and replaced in cache");
       }
     } else { // No cache hit
       const auto emplaceResult = m_cache.emplace(std::make_pair(key,
         cta::make_unique<TimestampedValue>(now, getNonCachedValue())));
-      return emplaceResult.first->second->value;
+      return ValueAndTimeBasedCacheInfo<Value>(emplaceResult.first->second->value, "First time value entered into cache");
     }
   }
 
diff --git a/catalogue/ValueAndTimeBasedCacheInfo.hpp b/catalogue/ValueAndTimeBasedCacheInfo.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc3680d13cff9856460d9eefbfd00914aea5a651
--- /dev/null
+++ b/catalogue/ValueAndTimeBasedCacheInfo.hpp
@@ -0,0 +1,36 @@
+/*
+ * @project        The CERN Tape Archive (CTA)
+ * @copyright      Copyright(C) 2015-2021 CERN
+ * @license        This program is free software: you can redistribute it and/or modify
+ *                 it under the terms of the GNU General Public License as published by
+ *                 the Free Software Foundation, either version 3 of the License, or
+ *                 (at your option) any later version.
+ *
+ *                 This program is distributed in the hope that it will be useful,
+ *                 but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *                 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *                 GNU General Public License for more details.
+ *
+ *                 You should have received a copy of the GNU General Public License
+ *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <string>
+#include <time.h>
+
+namespace cta {
+namespace catalogue {
+
+template <typename Value> struct ValueAndTimeBasedCacheInfo {
+  Value value;
+  std::string cacheInfo;
+
+  ValueAndTimeBasedCacheInfo(const Value &v, const std::string &cInfo):
+    value(v), cacheInfo(cInfo) {
+  }
+}; // class ValueAndTimeBasedCacheInfo
+
+} // namespace catalogue
+} // namespace cta
diff --git a/common/exception/UserErrorWithCacheInfo.hpp b/common/exception/UserErrorWithCacheInfo.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c7649d337f8776fd2f9678818da213c8b529be7
--- /dev/null
+++ b/common/exception/UserErrorWithCacheInfo.hpp
@@ -0,0 +1,50 @@
+/*
+ * @project        The CERN Tape Archive (CTA)
+ * @copyright      Copyright(C) 2015-2021 CERN
+ * @license        This program is free software: you can redistribute it and/or modify
+ *                 it under the terms of the GNU General Public License as published by
+ *                 the Free Software Foundation, either version 3 of the License, or
+ *                 (at your option) any later version.
+ *
+ *                 This program is distributed in the hope that it will be useful,
+ *                 but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *                 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *                 GNU General Public License for more details.
+ *
+ *                 You should have received a copy of the GNU General Public License
+ *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+namespace exception {
+
+/**
+ * A user error together with information about how the respective cached
+ * value was obtained.
+ */
+class UserErrorWithCacheInfo : public exception::UserError {
+public:
+  std::string cacheInfo;
+
+  /**
+   * Constructor.
+   *
+   * @param cInfo Information about how the respective cached value was
+   * obtained.
+   * @param context optional context string added to the message
+   * at initialisation time.
+   * @param embedBacktrace whether to embed a backtrace of where the
+   * exception was throw in the message
+   */
+  UserErrorWithCacheInfo(const std::string &cInfo, const std::string &context = "", const bool embedBacktrace = true):
+    UserError(context, embedBacktrace),
+    cacheInfo(cInfo) {
+  }
+}; // class UserErrorWithTimeBasedCacheInfo
+
+} // namespace exception
+} // namespace cta