diff --git a/Control/RootUtils/CMakeLists.txt b/Control/RootUtils/CMakeLists.txt
index fff981ad55c180da7bee92f3c4979b5ea432223a..e1eef70dde9acb25ea0868d868966b54126f6248 100644
--- a/Control/RootUtils/CMakeLists.txt
+++ b/Control/RootUtils/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 
 # Declare the package name:
 atlas_subdir( RootUtils )
@@ -56,5 +56,10 @@ atlas_add_test( TTreePatch_test
    SCRIPT ${Python_EXECUTABLE}
           ${CMAKE_CURRENT_SOURCE_DIR}/test/TTreePatch_t.py )
 
+atlas_add_test( WithRootErrorHandler_test
+   SOURCES test/WithRootErrorHandler_test.cxx
+   INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} 
+   LINK_LIBRARIES ${ROOT_LIBRARIES} RootUtils )
+
 # Install files from the package:
 atlas_install_python_modules( python/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} )
diff --git a/Control/RootUtils/RootUtils/WithRootErrorHandler.h b/Control/RootUtils/RootUtils/WithRootErrorHandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..30630cae1690c21cfc9d4de76ff8405c47e79f12
--- /dev/null
+++ b/Control/RootUtils/RootUtils/WithRootErrorHandler.h
@@ -0,0 +1,107 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+/*
+ * Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration.
+ */
+/**
+ * @file RootUtils/WithRootErrorHandler.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Mar, 2021
+ * @brief Run a MT piece of code with an alternate root error handler.
+ */
+
+
+#include "TError.h"
+#include <functional>
+#include <type_traits>
+
+
+#ifndef ROOTUTILS_WITHROOTERRORHANDLER_H
+#define ROOTUTILS_WITHROOTERRORHANDLER_H
+
+namespace RootUtils {
+
+
+/**
+ * @brief Run a MT piece of code with an alternate root error handler.
+ *
+ * Sometimes we want to run a piece of code with an alternate root
+ * error handler.  This is however problematic in MT jobs since the
+ * root error handler pointer is just a global variable visible
+ * in all threads.  We also frequently need to introduce additional
+ * globals if we need to communicate with the handler.
+ *
+ * This piece of code tries to get around these limitations.
+ * It installs a single root error handler during initialization.
+ * This handler will then dispatch to other handlers stored
+ * in thread-local storage.  This effectively makes changing
+ * the root error handler thread-local.  We also manage the handler
+ * as a std::function, allowing it to have associated state,
+ * and clean up the change using RAII.
+ *
+ * To change the root error handler, create an instance of this class,
+ * passing it a function object, which should have signature:
+ *
+ *@code
+ *  bool hand (int level, bool abort, const char* location, const char* msg)
+ @endcode
+ *
+ * The arguments are the same as for standard root error handlers.
+ * The handler should return true if previous handlers should also be called.
+ * If a handler returns false, no further handlers will be called.
+ *
+ * The change in the error handler is visible only in the current thread,
+ * and the revious handler will be restored when the object is deleted.
+ */
+class WithRootErrorHandler
+{
+public:
+  // A ROOT error handler has signature
+  //  void hand (int level, Bool_t abort, const char* location, const char* msg)
+
+  /// Type of handler to register.  Like a ROOT error handler, except that
+  /// it returns a bool.
+  using Handler_t = std::function<bool (int, Bool_t, const char*, const char*)>;
+  
+
+  /**
+   * @brief Temporarily install a thread-local root error handler.
+   * @param errhand The handler to be installed.
+   *
+   * The installed handler will only run in the current thread, and it will
+   * be removed when this object is deleted.  In addition to the usual
+   * arguments for a root error handler, it returns a bool.  If the returned
+   * value is true, previous handlers will also be executed; otherwise,
+   * no further handlers will be executed.
+   */
+  WithRootErrorHandler (Handler_t errhand);
+
+
+  /**
+   * @brief Destructor.
+   *
+   * Remove the error handler.
+   */
+  ~WithRootErrorHandler();
+
+
+  // Try to prevent misuse.
+  WithRootErrorHandler (const WithRootErrorHandler&) = delete;
+  WithRootErrorHandler (WithRootErrorHandler&&) = delete;
+  WithRootErrorHandler& operator= (const WithRootErrorHandler&) = delete;
+  WithRootErrorHandler& operator= (WithRootErrorHandler&&) = delete;
+
+  static void* operator new   (size_t) = delete;
+  static void* operator new[] (size_t) = delete;
+  static void  operator delete   (void*) = delete;
+  static void  operator delete[] (void*) = delete;
+
+
+private:
+  /// For error checking.
+  size_t m_size;
+};
+
+
+} // namespace RootUtils
+
+#endif // not ROOTUTILS_WITHROOTERRORHANDLER_H
diff --git a/Control/RootUtils/share/WithRootErrorHandler_test.ref b/Control/RootUtils/share/WithRootErrorHandler_test.ref
new file mode 100644
index 0000000000000000000000000000000000000000..804a09378002da98b1110cfd25c01b93bd711462
--- /dev/null
+++ b/Control/RootUtils/share/WithRootErrorHandler_test.ref
@@ -0,0 +1,9 @@
+RootUtils/WithRootErrorHandler_test
+test1
+Error in <foo1>: Bar1
+try1: Bar2
+Error in <foo2>: Bar2
+try2: Bar3
+try1: Bar3
+Error in <foo3>: Bar3
+try2: Bar4
diff --git a/Control/RootUtils/src/WithRootErrorHandler.cxx b/Control/RootUtils/src/WithRootErrorHandler.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4988e6f9a82fbf246acb4edfb5de53ba150fad94
--- /dev/null
+++ b/Control/RootUtils/src/WithRootErrorHandler.cxx
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration.
+ */
+/**
+ * @file RootUtils/src/WithRootErrorHandler.cxx
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Mar, 2021
+ * @brief Run a MT piece of code with an alternate root error handler.
+ */
+
+
+#include "RootUtils/WithRootErrorHandler.h"
+#include <vector>
+
+
+namespace {
+
+
+/// Thread-local list of registered error handlers, from oldest to newest.
+using Handler_t = RootUtils::WithRootErrorHandler::Handler_t;
+thread_local std::vector<Handler_t> rootErrorHandlers;
+
+
+/// Declare our own error handler to root during initialization
+/// and save the previous handler.
+void errorHandler (int level,
+                   Bool_t abort,
+                   const char* location,
+                   const char* msg);
+const ErrorHandlerFunc_t origHandler = ::SetErrorHandler (errorHandler);
+
+
+/**
+ * @brief Global root error handler.
+ */
+void errorHandler (int level,
+                   Bool_t abort,
+                   const char* location,
+                   const char* msg)
+{
+  // Execute all the handlers in our thread-local list from newest to oldest.
+  // Stop if one returns false.
+  for (int i = rootErrorHandlers.size()-1; i >= 0; --i) {
+    if (!rootErrorHandlers[i] (level, abort, location, msg)) return;
+  }
+  // They all returned true.  Call the previous handler.
+  origHandler (level, abort, location, msg);
+}
+
+
+} // anonymous namespace
+
+
+namespace RootUtils {
+
+
+/**
+ * @brief Temporarily install a thread-local root error handler.
+ * @param errhand The handler to be installed.
+ *
+ * The installed handler will only run in the current thread, and it will
+ * be removed when this object is deleted.  In addition to the usual
+ * arguments for a root error handler, it returns a bool.  If the returned
+ * value is true, previous handlers will also be executed; otherwise,
+ * no further handlers will be executed.
+ */
+WithRootErrorHandler::WithRootErrorHandler (Handler_t errhand)
+  : m_size (rootErrorHandlers.size()+1)
+{
+  rootErrorHandlers.push_back (errhand);
+}
+
+
+/**
+ * @brief Destructor.
+ *
+ * Remove the error handler.
+ */
+WithRootErrorHandler::~WithRootErrorHandler()
+{
+  if (m_size != rootErrorHandlers.size()) std::abort();
+  rootErrorHandlers.pop_back();
+}
+
+
+} // namespace RootUtils
diff --git a/Control/RootUtils/test/WithRootErrorHandler_test.cxx b/Control/RootUtils/test/WithRootErrorHandler_test.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..703bb8e1d21141c849d702877f08af0a8f49bdb3
--- /dev/null
+++ b/Control/RootUtils/test/WithRootErrorHandler_test.cxx
@@ -0,0 +1,77 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+/**
+ * @file RootUtils/test/WithRootErrorHandler_test.cxx
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Mar, 2021
+ * @brief Tests for WithRootErrorHandler.
+ */
+
+
+#undef NDEBUG
+#include "RootUtils/WithRootErrorHandler.h"
+#include "TError.h"
+#include <string>
+#include <iostream>
+#include <cassert>
+
+
+struct TestHand
+{
+  TestHand (const std::string& name, bool passOn)
+    : m_name (name), m_passOn (passOn)
+  { }
+  bool operator() (int level, Bool_t abort, const char* loc, const char* msg);
+
+  std::string m_name;
+  bool m_passOn;
+};
+
+
+bool TestHand::operator() (int /*level*/, Bool_t /*abort*/,
+                           const char* /*loc*/, const char* msg)
+{
+  std::cout << m_name << ": " << msg << "\n";
+  std::cout.flush();
+  return m_passOn;
+}
+
+
+void test1()
+{
+  std::cout << "test1\n";
+  std::cout.flush();
+
+  ::Error ("foo1", "Bar1");
+  std::cerr.flush();
+  std::cout.flush();
+  {
+    RootUtils::WithRootErrorHandler hand { TestHand("try1", true) };
+    ::Error ("foo2", "Bar2");
+    std::cerr.flush();
+    std::cout.flush();
+  }
+  {
+    RootUtils::WithRootErrorHandler hand1 { TestHand("try1", true) };
+    RootUtils::WithRootErrorHandler hand2 { TestHand("try2", true) };
+    ::Error ("foo3", "Bar3");
+    std::cerr.flush();
+    std::cout.flush();
+  }
+  {
+    RootUtils::WithRootErrorHandler hand1 { TestHand("try1", true) };
+    RootUtils::WithRootErrorHandler hand2 { TestHand("try2", false) };
+    ::Error ("foo4", "Bar4");
+    std::cerr.flush();
+    std::cout.flush();
+  }
+}
+
+
+int main()
+{
+  std::cout << "RootUtils/WithRootErrorHandler_test\n";
+  test1();
+  return 0;
+}