diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp
index 2420eb4dba02662207ec8e0e15e2300c36d72ab2..e555ca224c1d02f11e11696542774f2e470b6432 100644
--- a/src/mesh/GmshReader.cpp
+++ b/src/mesh/GmshReader.cpp
@@ -1516,6 +1516,7 @@ GmshReader::__proceedData()
       xr[i][2] = m_mesh_data.__vertices[i][2];
     }
     m_mesh = std::make_shared<MeshType>(p_connectivity, xr);
+    this->_checkMesh<3>();
 
   } else if (dot(dimension2_mask, elementNumber) > 0) {
     const size_t nb_cells = dot(dimension2_mask, elementNumber);
@@ -1535,6 +1536,7 @@ GmshReader::__proceedData()
       xr[i][1] = m_mesh_data.__vertices[i][1];
     }
     m_mesh = std::make_shared<MeshType>(p_connectivity, xr);
+    this->_checkMesh<2>();
 
   } else if (dot(dimension1_mask, elementNumber) > 0) {
     const size_t nb_cells = dot(dimension1_mask, elementNumber);
@@ -1554,6 +1556,7 @@ GmshReader::__proceedData()
     }
 
     m_mesh = std::make_shared<MeshType>(p_connectivity, xr);
+    this->_checkMesh<1>();
 
   } else {
     throw NormalError("using a dimension 0 mesh is forbidden");
diff --git a/src/mesh/MeshBuilderBase.cpp b/src/mesh/MeshBuilderBase.cpp
index 9e62baf56e7915379d65a5c88c21fe7411fca253..a3a8440bd25499d8aea34a08e7c836b66a4c3a32 100644
--- a/src/mesh/MeshBuilderBase.cpp
+++ b/src/mesh/MeshBuilderBase.cpp
@@ -5,12 +5,14 @@
 #include <mesh/ConnectivityDispatcher.hpp>
 #include <mesh/ItemId.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 
 #include <vector>
 
-template <int Dimension>
+template <size_t Dimension>
 void
 MeshBuilderBase::_dispatch()
 {
@@ -28,8 +30,8 @@ MeshBuilderBase::_dispatch()
     NodeValue<Rd> xr;
     m_mesh = std::make_shared<MeshType>(connectivity, xr);
   }
-  const MeshType& mesh = static_cast<const MeshType&>(*m_mesh);
 
+  const MeshType& mesh = dynamic_cast<const MeshType&>(*m_mesh);
   ConnectivityDispatcher<Dimension> dispatcher(mesh.connectivity());
 
   std::shared_ptr dispatched_connectivity = dispatcher.dispatchedConnectivity();
@@ -41,3 +43,165 @@ MeshBuilderBase::_dispatch()
 template void MeshBuilderBase::_dispatch<1>();
 template void MeshBuilderBase::_dispatch<2>();
 template void MeshBuilderBase::_dispatch<3>();
+
+template <size_t Dimension>
+void
+MeshBuilderBase::_checkMesh() const
+{
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  if (not m_mesh) {
+    throw UnexpectedError("mesh is not built yet");
+  }
+
+  const MeshType& mesh = dynamic_cast<const MeshType&>(*m_mesh);
+
+  const ConnectivityType& connectivity = mesh.connectivity();
+
+  if constexpr (Dimension > 2) {   // check for duplicated edges
+    auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+
+    std::vector<std::vector<EdgeId>> node_edges(mesh.numberOfNodes());
+    for (EdgeId edge_id = 0; edge_id < mesh.numberOfEdges(); ++edge_id) {
+      auto node_list = edge_to_node_matrix[edge_id];
+      for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+        node_edges[node_list[i_node]].push_back(edge_id);
+      }
+    }
+
+    for (auto&& edge_list : node_edges) {
+      std::sort(edge_list.begin(), edge_list.end());
+    }
+
+    for (EdgeId edge_id = 0; edge_id < mesh.numberOfEdges(); ++edge_id) {
+      auto node_list                   = edge_to_node_matrix[edge_id];
+      std::vector<EdgeId> intersection = node_edges[node_list[0]];
+      for (size_t i_node = 1; i_node < node_list.size(); ++i_node) {
+        std::vector<EdgeId> local_intersection;
+        std::set_intersection(intersection.begin(), intersection.end(),   //
+                              node_edges[node_list[i_node]].begin(), node_edges[node_list[i_node]].end(),
+                              std::back_inserter(local_intersection));
+        std::swap(local_intersection, intersection);
+        if (intersection.size() < 2) {
+          break;
+        }
+      }
+
+      if (intersection.size() > 1) {
+        std::ostringstream error_msg;
+        error_msg << "invalid mesh.\n\tFollowing edges\n";
+        for (EdgeId edge_id : intersection) {
+          error_msg << "\t - id=" << edge_id << " number=" << connectivity.edgeNumber()[edge_id] << '\n';
+        }
+        error_msg << "\tare duplicated";
+        throw NormalError(error_msg.str());
+      }
+    }
+  }
+
+  if constexpr (Dimension > 1) {   // check for duplicated faces
+    auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+
+    std::vector<std::vector<FaceId>> node_faces(mesh.numberOfNodes());
+    for (FaceId face_id = 0; face_id < mesh.numberOfFaces(); ++face_id) {
+      auto node_list = face_to_node_matrix[face_id];
+      for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+        node_faces[node_list[i_node]].push_back(face_id);
+      }
+    }
+
+    for (auto&& face_list : node_faces) {
+      std::sort(face_list.begin(), face_list.end());
+    }
+
+    for (FaceId face_id = 0; face_id < mesh.numberOfFaces(); ++face_id) {
+      auto node_list                   = face_to_node_matrix[face_id];
+      std::vector<FaceId> intersection = node_faces[node_list[0]];
+      for (size_t i_node = 1; i_node < node_list.size(); ++i_node) {
+        std::vector<FaceId> local_intersection;
+        std::set_intersection(intersection.begin(), intersection.end(),   //
+                              node_faces[node_list[i_node]].begin(), node_faces[node_list[i_node]].end(),
+                              std::back_inserter(local_intersection));
+        std::swap(local_intersection, intersection);
+        if (intersection.size() < 2) {
+          break;
+        }
+      }
+
+      if (intersection.size() > 1) {
+        std::ostringstream error_msg;
+        error_msg << "invalid mesh.\n\tFollowing faces\n";
+        for (FaceId face_id : intersection) {
+          error_msg << "\t - id=" << face_id << " number=" << connectivity.faceNumber()[face_id] << '\n';
+        }
+        error_msg << "\tare duplicated";
+        throw NormalError(error_msg.str());
+      }
+    }
+  }
+
+  auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+
+  {   // check for duplicated cells
+    std::vector<std::vector<CellId>> node_cells(mesh.numberOfNodes());
+    for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+      auto node_list = cell_to_node_matrix[cell_id];
+      for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+        node_cells[node_list[i_node]].push_back(cell_id);
+      }
+    }
+
+    for (auto&& cell_list : node_cells) {
+      std::sort(cell_list.begin(), cell_list.end());
+    }
+
+    for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+      auto node_list                   = cell_to_node_matrix[cell_id];
+      std::vector<CellId> intersection = node_cells[node_list[0]];
+      for (size_t i_node = 1; i_node < node_list.size(); ++i_node) {
+        std::vector<CellId> local_intersection;
+        std::set_intersection(intersection.begin(), intersection.end(),   //
+                              node_cells[node_list[i_node]].begin(), node_cells[node_list[i_node]].end(),
+                              std::back_inserter(local_intersection));
+        std::swap(local_intersection, intersection);
+        if (intersection.size() < 2) {
+          break;
+        }
+      }
+
+      if (intersection.size() > 1) {
+        std::ostringstream error_msg;
+        error_msg << "invalid mesh.\n\tFollowing cells\n";
+        for (CellId cell_id : intersection) {
+          error_msg << "\t - id=" << cell_id << " number=" << connectivity.cellNumber()[cell_id] << '\n';
+        }
+        error_msg << "\tare duplicated";
+        throw NormalError(error_msg.str());
+      }
+    }
+  }
+
+  const auto& Cjr = MeshDataManager::instance().getMeshData(mesh).Cjr();
+  const auto& xr  = mesh.xr();
+
+  for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+    double cell_volume = 0;
+    auto cell_nodes    = cell_to_node_matrix[cell_id];
+    for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) {
+      cell_volume += dot(Cjr(cell_id, i_node), xr[cell_nodes[i_node]]);
+    }
+
+    if (cell_volume <= 0) {
+      std::ostringstream error_msg;
+      error_msg << "invalid mesh.\n\tThe following cell\n";
+      error_msg << "\t - id=" << cell_id << " number=" << connectivity.cellNumber()[cell_id] << '\n';
+      error_msg << "\thas non-positive volume: " << cell_volume / Dimension;
+      throw NormalError(error_msg.str());
+    }
+  }
+}
+
+template void MeshBuilderBase::_checkMesh<1>() const;
+template void MeshBuilderBase::_checkMesh<2>() const;
+template void MeshBuilderBase::_checkMesh<3>() const;
diff --git a/src/mesh/MeshBuilderBase.hpp b/src/mesh/MeshBuilderBase.hpp
index 6c0328bb8ee624e19d42fb4660ba3665ff7164de..63c55ec565b1f48023906f5aa602db13ef76d62c 100644
--- a/src/mesh/MeshBuilderBase.hpp
+++ b/src/mesh/MeshBuilderBase.hpp
@@ -10,9 +10,12 @@ class MeshBuilderBase
  protected:
   std::shared_ptr<const IMesh> m_mesh;
 
-  template <int Dimension>
+  template <size_t Dimension>
   void _dispatch();
 
+  template <size_t Dimension>
+  void _checkMesh() const;
+
  public:
   std::shared_ptr<const IMesh>
   mesh() const