diff --git a/include/aidge/operator/MetaOperator.hpp b/include/aidge/operator/MetaOperator.hpp
index 6018c7c154095bf252f48dff24dae725f3157914..e7329ec15f91f941b0f7af1adca145f4260d53fa 100644
--- a/include/aidge/operator/MetaOperator.hpp
+++ b/include/aidge/operator/MetaOperator.hpp
@@ -65,6 +65,9 @@ public:
             outputNodes.push_back(*mGraph->outputNodes().begin());
         }
 
+        AIDGE_ASSERT(mGraph->inputNodes().size() == inputNodes.size(), "wrong number of specified input nodes");
+        AIDGE_ASSERT(mGraph->outputNodes().size() == outputNodes.size(), "wrong number of specified output nodes");
+
         // Identify inputs that are outside the micro-graph
         for (const auto& inputNode : inputNodes) {
             AIDGE_ASSERT(mGraph->inView(inputNode), "input node must be in the graph");
@@ -96,6 +99,9 @@ public:
                 ++outputIdx;
             }
         }
+
+        AIDGE_INTERNAL_ASSERT(mInputOps.size() == mGraph->inputs().size());
+        AIDGE_INTERNAL_ASSERT(mOutputOps.size() == mGraph->outputs().size());
     }
 
     /**
@@ -296,8 +302,8 @@ inline std::shared_ptr<Node> PaddedConv(DimSize_t in_channels,
 
     // Graph has to be created manually in order to exclude Producers from the graph
     auto graph = std::make_shared<GraphView>();
-    graph->add(pad, false);
-    graph->add(conv, false);
+    graph->add(pad);
+    graph->add(conv, false); // exclude Producers, as they should be inputs of the meta-op
 
     // Need to specify the ordered list of input operators
     const std::vector<NodePtr> orderedInputNodes = {pad, conv};
diff --git a/include/aidge/utils/Utils.hpp b/include/aidge/utils/ErrorHandling.hpp
similarity index 56%
rename from include/aidge/utils/Utils.hpp
rename to include/aidge/utils/ErrorHandling.hpp
index 5facc80eac0773db3879b08f7708281ec9a9e388..85d059831249d2f245cf7631b9048cde8a80e27d 100644
--- a/include/aidge/utils/Utils.hpp
+++ b/include/aidge/utils/ErrorHandling.hpp
@@ -10,12 +10,15 @@
  ********************************************************************************/
 
 
-#ifndef AIDGE_UTILS_H_
-#define AIDGE_UTILS_H_
+#ifndef AIDGE_ERRORHANDLING_H_
+#define AIDGE_ERRORHANDLING_H_
 
 #include <cstdio>
 
-#ifdef NO_EXCEPTIONS
+#define AIDGE_STRINGIZE_DETAIL(x) #x
+#define AIDGE_STRINGIZE(x) AIDGE_STRINGIZE_DETAIL(x)
+
+#ifdef NO_EXCEPTION
 #define AIDGE_THROW_OR_ABORT(ex, ...) \
 do { std::printf(__VA_ARGS__); std::abort(); } while (false)
 #else
@@ -35,7 +38,21 @@ do { \
 } while (false)
 #endif
 
+/**
+ * Macro for specified API assertions.
+ * Used to check logic directly related to user's inputs.
+ * If it asserts, it means an user error.
+*/
 #define AIDGE_ASSERT(stm, ...) \
-if (!(stm)) { AIDGE_THROW_OR_ABORT(std::runtime_error, __VA_ARGS__); }
+if (!(stm)) { printf("Assertion failed: " AIDGE_STRINGIZE(stm) " in " __FILE__ ":%d", __LINE__); \
+    AIDGE_THROW_OR_ABORT(std::runtime_error, __VA_ARGS__); }
+
+/**
+ * Macro for internal assertions.
+ * Used to check internal logic not directly related to API user's inputs.
+ * If it asserts, it means a bug.
+*/
+#define AIDGE_INTERNAL_ASSERT(stm) \
+assert((stm) && "Internal assertion failed: " #stm " in " __FILE__ ":" AIDGE_STRINGIZE(__LINE__))
 
-#endif //AIDGE_UTILS_H_
+#endif //AIDGE_ERRORHANDLING_H_
diff --git a/include/aidge/utils/StaticAttributes.hpp b/include/aidge/utils/StaticAttributes.hpp
index fb800cffbcff5d4113961f8e62977417336f2cb8..b6dd913353c02f888ba085f120f8dda6db078737 100644
--- a/include/aidge/utils/StaticAttributes.hpp
+++ b/include/aidge/utils/StaticAttributes.hpp
@@ -18,7 +18,7 @@
 #include <typeinfo>
 
 #include "aidge/utils/Attributes.hpp"
-#include "aidge/utils/Utils.hpp"
+#include "aidge/utils/ErrorHandling.hpp"
 
 namespace Aidge {
 /**