diff --git a/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc b/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc
index 0879b6241ff26516a5aecc9c7300b11f7ddf3fe8..4ed7b9be9ed2f8b99c8d8db1a5635f3c766edb1f 100644
--- a/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc
+++ b/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019-2024 Dynare Team
+ * Copyright © 2019-2025 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -26,6 +26,7 @@
 using namespace std::literals::string_literals;
 
 std::string DynamicModelCaller::error_msg;
+std::mutex DynamicModelCaller::error_mtx;
 
 #if !defined(_WIN32) && !defined(__CYGWIN32__)
 void* DynamicModelDllCaller::resid_mex {nullptr};
@@ -210,12 +211,14 @@ DynamicModelMatlabCaller::eval(double* resid)
                                               funcname.c_str())};
     if (exception)
       {
+        std::lock_guard lk {error_mtx};
         error_msg = "An error occurred when calling " + funcname;
         return; // Avoid manipulating null pointers in plhs, see #1832
       }
 
     if (!mxIsDouble(plhs[0]) || mxIsSparse(plhs[0]))
       {
+        std::lock_guard lk {error_mtx};
         error_msg = "Residuals should be a dense array of double floats";
         return;
       }
@@ -249,6 +252,7 @@ DynamicModelMatlabCaller::eval(double* resid)
                                                 funcname.c_str())};
       if (exception)
         {
+          std::lock_guard lk {error_mtx};
           error_msg = "An error occurred when calling " + funcname;
           return; // Avoid manipulating null pointers in plhs, see #1832
         }
@@ -261,6 +265,7 @@ DynamicModelMatlabCaller::eval(double* resid)
 
       if (!mxIsDouble(plhs[0]) || !mxIsSparse(plhs[0]))
         {
+          std::lock_guard lk {error_mtx};
           error_msg = "Jacobian should be a sparse array of double floats";
           return;
         }
diff --git a/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh b/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh
index b651ec59a23f9334cca815bff2595c0023523580..09612b4d69b4a28f4b200f57fd23a0e5da147dfa 100644
--- a/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh
+++ b/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019-2024 Dynare Team
+ * Copyright © 2019-2025 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <limits>
 #include <memory>
+#include <mutex>
 #include <string>
 #include <type_traits>
 #include <vector>
@@ -47,6 +48,7 @@ public:
 
   // Used to store error messages (as exceptions cannot cross the OpenMP boundary)
   static std::string error_msg;
+  static std::mutex error_mtx; // Guard for access in OpenMP context
 
   DynamicModelCaller(bool linear_arg, bool compute_jacobian_arg) :
       linear {linear_arg}, compute_jacobian {compute_jacobian_arg}