diff --git a/src/language/BuiltinFunctionEmbedder.hpp b/src/language/BuiltinFunctionEmbedder.hpp
index 08ee68302ee91aea203f5a237e8cab1a6d8a54df..8d2bcbf0721b26fee4526e9acb503a5c75615302 100644
--- a/src/language/BuiltinFunctionEmbedder.hpp
+++ b/src/language/BuiltinFunctionEmbedder.hpp
@@ -2,16 +2,15 @@
 #define BUILTIN_FUNCTION_EMBEDDER_HPP
 
 #include <language/ASTNodeDataType.hpp>
-
 #include <language/DataHandler.hpp>
 #include <language/DataVariant.hpp>
 #include <language/FunctionTable.hpp>
 #include <utils/Demangle.hpp>
+#include <utils/Exceptions.hpp>
 #include <utils/PugsTraits.hpp>
 
 #include <functional>
 #include <memory>
-#include <sstream>
 #include <vector>
 
 template <typename T>
@@ -70,21 +69,19 @@ class BuiltinFunctionEmbedder : public IBuiltinFunctionEmbedder
             auto data_handler = static_cast<const DataHandler<typename Ti_Type::element_type>&>(v_i.get());
             std::get<I>(t)    = data_handler.data_ptr();
           } else {
-            throw std::runtime_error("unexpected argument types while casting: expecting EmbeddedData");
+            throw UnexpectedError("unexpected argument types while casting: expecting EmbeddedData");
           }
         } else if constexpr (std::is_same_v<Ti_Type, FunctionId>) {
           if constexpr (std::is_same_v<Vi_Type, uint64_t>) {
             std::get<I>(t) = FunctionId{v_i};
-            throw std::runtime_error(
-              "WIP: should get better descriptor, FunctionId should at least refer to the symbol table.");
+            throw NotImplementedError(
+              "Should get better descriptor, FunctionId should at least refer to the symbol table.");
           } else {
-            throw std::runtime_error("unexpected argument types while casting: expecting uint64");
+            throw UnexpectedError("unexpected argument types while casting: expecting uint64");
           }
         } else {
-          std::ostringstream os;
-          os << "unexpected argument types while casting " << demangle<Vi_Type>() << " -> " << demangle<Ti_Type>()
-             << std::ends;
-          throw std::runtime_error(os.str());
+          throw UnexpectedError("Unexpected argument types while casting " + demangle<Vi_Type>() + " -> " +
+                                demangle<Ti_Type>());
         }
       },
       v[I]);
diff --git a/src/language/BuiltinModule.cpp b/src/language/BuiltinModule.cpp
index 481becf568c014d7ec3c99814313a7cc9b62dd3e..93908f66cec2e797b0bf69f4a5368b6a447fab92 100644
--- a/src/language/BuiltinModule.cpp
+++ b/src/language/BuiltinModule.cpp
@@ -2,6 +2,7 @@
 
 #include <language/BuiltinFunctionEmbedder.hpp>
 #include <language/TypeDescriptor.hpp>
+#include <utils/Exceptions.hpp>
 
 #include <iostream>
 
@@ -11,22 +12,16 @@ BuiltinModule::_addBuiltinFunction(const std::string& name,
 {
   auto [i_builtin_function, success] =
     m_name_builtin_function_map.insert(std::make_pair(name, builtin_function_embedder));
-  // LCOV_EXCL_START
   if (not success) {
-    std::cerr << "builtin-function '" << name << "' cannot be added!\n";
-    std::exit(1);
+    throw NormalError("builtin-function '" + name + "' cannot be added!\n");
   }
-  // LCOV_EXCL_STOP
 }
 
 void
 BuiltinModule::_addTypeDescriptor(std::shared_ptr<TypeDescriptor> type_descriptor)
 {
   auto [i_type, success] = m_name_type_map.insert(std::make_pair(type_descriptor->name(), type_descriptor));
-  // LCOV_EXCL_START
   if (not success) {
-    std::cerr << "type '" << type_descriptor->name() << "' cannot be added!\n";
-    std::exit(1);
+    throw NormalError("type '" + type_descriptor->name() + "' cannot be added!\n");
   }
-  // LCOV_EXCL_STOP
 }
diff --git a/src/language/MeshModule.cpp b/src/language/MeshModule.cpp
index b85db7c0b42b4a28731214410fe79895113f8807..c3f5d4916485b3018dc0bc63122f3d3d0477578a 100644
--- a/src/language/MeshModule.cpp
+++ b/src/language/MeshModule.cpp
@@ -6,6 +6,7 @@
 #include <mesh/Connectivity.hpp>
 #include <mesh/GmshReader.hpp>
 #include <mesh/Mesh.hpp>
+#include <utils/Exceptions.hpp>
 
 #include <output/VTKWriter.hpp>
 
@@ -39,11 +40,11 @@ MeshModule::MeshModule()
                               [](std::shared_ptr<IMesh> p_mesh, FunctionId function_id) -> std::shared_ptr<IMesh> {
                                 switch (p_mesh->dimension()) {
                                 case 1: {
-                                  throw std::runtime_error("not implemented in 1d");
+                                  throw NotImplementedError("not implemented in 1d");
                                   break;
                                 }
                                 case 2: {
-                                  throw std::runtime_error("not implemented in 2d");
+                                  throw NotImplementedError("not implemented in 2d");
                                   break;
                                 }
                                 case 3: {
diff --git a/src/language/PugsParser.cpp b/src/language/PugsParser.cpp
index 96e01e49a225d3c334e4c493f48f44f16fbdaf15..8077d1737a7840261679cee3e1291effac903947 100644
--- a/src/language/PugsParser.cpp
+++ b/src/language/PugsParser.cpp
@@ -16,6 +16,7 @@
 #include <language/ASTSymbolTableBuilder.hpp>
 #include <language/PEGGrammar.hpp>
 #include <language/SymbolTable.hpp>
+#include <utils/Exceptions.hpp>
 #include <utils/PugsAssert.hpp>
 
 #include <pegtl/contrib/analyze.hpp>
@@ -26,6 +27,7 @@
 
 #include <fstream>
 #include <iostream>
+#include <sstream>
 #include <unordered_map>
 #include <variant>
 
@@ -83,12 +85,14 @@ parser(const std::string& filename)
   }
   catch (const parse_error& e) {
     const auto p = e.positions.front();
-    std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.byte_in_line << ": " << rang::style::reset
-              << rang::fgB::red << "error: " << rang::fg::reset << rang::style::bold << e.what() << rang::style::reset
-              << '\n'
-              << input.line_at(p) << '\n'
-              << std::string(p.byte_in_line, ' ') << rang::fgB::yellow << '^' << rang::fg::reset << std::endl;
-    std::exit(1);
+
+    std::ostringstream os;
+    os << rang::style::bold << p.source << ':' << p.line << ':' << p.byte_in_line << ": " << rang::style::reset
+       << rang::fgB::red << "error: " << rang::fg::reset << rang::style::bold << e.what() << rang::style::reset << '\n'
+       << input.line_at(p) << '\n'
+       << std::string(p.byte_in_line, ' ') << rang::fgB::yellow << '^' << rang::fg::reset;
+
+    throw RawError(os.str());
   }
 
   std::cout << "Parsed: " << filename << '\n';