diff --git a/include/communication/bolt/v1/states/error.hpp b/include/communication/bolt/v1/states/error.hpp
index 0748fc5e5..6c9b37d6c 100644
--- a/include/communication/bolt/v1/states/error.hpp
+++ b/include/communication/bolt/v1/states/error.hpp
@@ -9,6 +9,8 @@ namespace bolt
 class Error : public State
 {
 public:
+    Error();
+
     State *run(Session &session) override;
 };
 
diff --git a/include/communication/bolt/v1/states/executor.hpp b/include/communication/bolt/v1/states/executor.hpp
index 6183dd457..e614d57d2 100644
--- a/include/communication/bolt/v1/states/executor.hpp
+++ b/include/communication/bolt/v1/states/executor.hpp
@@ -20,12 +20,10 @@ public:
     State* run(Session& session) override final;
 
 protected:
-    Logger logger;
-
     /* Execute an incoming query
      *
      */
-    void run(Session& session, Query& query);
+    State* run(Session& session, Query& query);
 
     /* Send all remaining results to the client
      *
diff --git a/include/communication/bolt/v1/states/state.hpp b/include/communication/bolt/v1/states/state.hpp
index 50a38494c..2915cc29d 100644
--- a/include/communication/bolt/v1/states/state.hpp
+++ b/include/communication/bolt/v1/states/state.hpp
@@ -4,6 +4,8 @@
 #include <cstdint>
 #include <memory>
 
+#include "logging/default.hpp"
+
 namespace bolt
 {
 
@@ -15,9 +17,14 @@ public:
     using uptr = std::unique_ptr<State>;
 
     State() = default;
+    State(Logger logger) : logger(logger) {}
+
     virtual ~State() = default;
 
     virtual State* run(Session& session) = 0;
+
+protected:
+    Logger logger;
 };
 
 }
diff --git a/include/communication/bolt/v1/transport/bolt_encoder.hpp b/include/communication/bolt/v1/transport/bolt_encoder.hpp
index dba6ab9be..8b35dec3e 100644
--- a/include/communication/bolt/v1/transport/bolt_encoder.hpp
+++ b/include/communication/bolt/v1/transport/bolt_encoder.hpp
@@ -249,7 +249,7 @@ public:
 
     void message_ignored()
     {
-        write_struct_header(1);
+        write_struct_header(0);
         write(underlying_cast(MessageCode::Ignored));
     }
 
diff --git a/include/query_engine/code_generator/cypher_state.hpp b/include/query_engine/code_generator/cypher_state.hpp
index ea4a2ebab..91b701b0b 100644
--- a/include/query_engine/code_generator/cypher_state.hpp
+++ b/include/query_engine/code_generator/cypher_state.hpp
@@ -42,6 +42,7 @@ enum class EntitySource : uint8_t
     None,
     InternalId,
     LabelIndex,
+    TypeIndex,
     MainStorage 
 };
 
diff --git a/include/query_engine/code_generator/entity_search.hpp b/include/query_engine/code_generator/entity_search.hpp
index 3db500d48..30f06ea62 100644
--- a/include/query_engine/code_generator/entity_search.hpp
+++ b/include/query_engine/code_generator/entity_search.hpp
@@ -26,6 +26,7 @@ using cost_t = uint64_t;
 constexpr cost_t internal_id_cost = 10;
 constexpr cost_t property_cost = 100;
 constexpr cost_t label_cost = 1000;
+constexpr cost_t type_cost = 1000;
 constexpr cost_t max_cost = max<cost_t>();
 
 template <typename T>
@@ -36,6 +37,7 @@ public:
     {
         internal_id,
         label_index,
+        type_index,
         property_index,
         main_storage
     };
@@ -47,6 +49,7 @@ public:
     {
         costs[SearchPlace::internal_id] = max<T>();
         costs[SearchPlace::label_index] = max<T>();
+        costs[SearchPlace::type_index] = max<T>();
         costs[SearchPlace::property_index] = max<T>();
         costs[SearchPlace::main_storage] = max<T>();
     }
@@ -80,6 +83,7 @@ using search_cost_t = SearchCost<cost_t>;
 
 constexpr auto search_internal_id = search_cost_t::SearchPlace::internal_id;
 constexpr auto search_label_index = search_cost_t::SearchPlace::label_index;
+constexpr auto search_type_index = search_cost_t::SearchPlace::type_index;
 constexpr auto search_property_index =
     search_cost_t::SearchPlace::property_index;
 constexpr auto search_main_storage = search_cost_t::SearchPlace::main_storage;
diff --git a/include/query_engine/code_generator/handlers/match.hpp b/include/query_engine/code_generator/handlers/match.hpp
index c6e928e4b..eeb1ce4bc 100644
--- a/include/query_engine/code_generator/handlers/match.hpp
+++ b/include/query_engine/code_generator/handlers/match.hpp
@@ -32,6 +32,7 @@ auto match_query_action =
 
         auto name = kv.first;
 
+        // TODO: duplicated code -> BIG PROBLEM
         // find node
         if (kv.second == ClauseAction::MatchNode) {
             if (already_matched(cypher_data, name, EntityType::Node))
@@ -69,6 +70,13 @@ auto match_query_action =
             if (place == entity_search::search_main_storage) {
                 cypher_data.source(name, EntitySource::MainStorage);
             }
+            if (place == entity_search::search_type_index) {
+                if (action_data.entity_data.at(name).tags.size() > 1) {
+                    throw SemanticError("Multiple type match (currently NOT supported)");
+                }
+                cypher_data.source(name, EntitySource::TypeIndex);
+                cypher_data.tags(name, action_data.entity_data.at(name).tags);
+            }
         }
     }
 
diff --git a/include/query_engine/code_generator/handlers/return.hpp b/include/query_engine/code_generator/handlers/return.hpp
index e4d631fd4..6a77c9615 100644
--- a/include/query_engine/code_generator/handlers/return.hpp
+++ b/include/query_engine/code_generator/handlers/return.hpp
@@ -43,12 +43,23 @@ auto return_query_action =
             {
                 if (cypher_data.type(entity) == EntityType::Node) {
                     if (cypher_data.tags(entity).size() == 0)
-                        throw CppGeneratorException("entity has no tags");
+                        throw CppGeneratorException("node has no labels");
                     auto label = cypher_data.tags(entity).at(0);  
-                    code += code_line(code::fine_and_write_vertices_by_label,
+                    code += code_line(code::find_and_write_vertices_by_label,
                                       entity, label);
                 }
             }
+
+            if (cypher_data.source(entity) == EntitySource::TypeIndex)
+            {
+                if (cypher_data.type(entity) == EntityType::Relationship) {
+                    if (cypher_data.tags(entity).size() == 0)
+                        throw CppGeneratorException("edge has no tag");
+                    auto type = cypher_data.tags(entity).at(0);  
+                    code += code_line(code::find_and_write_edges_by_type,
+                                      entity, type);
+                }
+            }
         } 
         else if (element.is_projection()) 
         {
diff --git a/include/query_engine/hardcode/queries.hpp b/include/query_engine/hardcode/queries.hpp
index 6424b745a..2ef4ab2c7 100644
--- a/include/query_engine/hardcode/queries.hpp
+++ b/include/query_engine/hardcode/queries.hpp
@@ -332,13 +332,13 @@ auto load_queries(Db &db)
         if (it_type.count() > it_vertex.count()) {
             // Going through vertices wiil probably be faster
             it_vertex.to().for_all([&](auto m) {
-                // PRINT n,m
+                // PRINT n, m
             });
 
         } else {
             // Going through edges wiil probably be faster
             it_type.to().for_all([&](auto m) {
-                // PRINT n,m
+                // PRINT n, m
             });
         }
 
diff --git a/include/query_engine/traverser/code.hpp b/include/query_engine/traverser/code.hpp
index 5bdd58834..23b8d6b5d 100644
--- a/include/query_engine/traverser/code.hpp
+++ b/include/query_engine/traverser/code.hpp
@@ -71,7 +71,7 @@ const std::string write_all_vertices =
         "        }});\n"
         "        stream.write_meta(\"rw\");\n";
 
-const std::string fine_and_write_vertices_by_label =
+const std::string find_and_write_vertices_by_label =
         "auto &label = t.label_find_or_create(\"{1}\");\n"
         "        stream.write_field(\"{0}\");\n"
         "        label.index().for_range(t)->for_all([&](auto vertex) {{\n"
@@ -94,6 +94,17 @@ const std::string write_all_edges =
         "        }});\n"
         "        stream.write_meta(\"rw\");\n";
 
+const std::string find_and_write_edges_by_type =
+        "auto &type = t.type_find_or_create(\"{1}\");\n"
+        "        stream.write_field(\"{0}\");\n"
+        "        type.index().for_range(t)->for_all([&](auto edge) {{\n"
+        "            stream.write_record();\n"
+        "            stream.write_list_header(1);\n"
+        "            stream.write(edge);\n"
+        "            stream.chunk();\n"
+        "        }});\n"
+        "        stream.write_meta(\"rw\");\n";
+
 const std::string return_true = "return true;";
 
 const std::string todo = "// TODO: {}";
diff --git a/include/query_engine/traverser/cpp_traverser.hpp b/include/query_engine/traverser/cpp_traverser.hpp
index 466500943..15e2bbfef 100644
--- a/include/query_engine/traverser/cpp_traverser.hpp
+++ b/include/query_engine/traverser/cpp_traverser.hpp
@@ -358,11 +358,14 @@ public:
 
     void visit(ast::RelationshipTypeList &ast_relationship_type_list) override
     {
-        auto &data = generator.action_data();
+        auto &action_data = generator.action_data();
 
         if (ast_relationship_type_list.has_value()) {
             auto type = ast_relationship_type_list.value->name;
-            data.add_entity_tag(entity, type);
+            action_data.add_entity_tag(entity, type);
+            action_data.csm.search_cost(
+                entity, entity_search::search_type_index,
+                entity_search::type_cost);
         }
 
         Traverser::visit(ast_relationship_type_list);
diff --git a/include/utils/exceptions/basic_exception.hpp b/include/utils/exceptions/basic_exception.hpp
index 79ea3d5f9..f358b17ee 100644
--- a/include/utils/exceptions/basic_exception.hpp
+++ b/include/utils/exceptions/basic_exception.hpp
@@ -9,16 +9,26 @@
 class BasicException : public std::exception
 {
 public:
-    BasicException(const std::string& message) noexcept : message(message)
+    BasicException(const std::string& message) noexcept
+        : message(message),
+          stacktrace_size(3)
     {
 #ifndef NDEBUG
         this->message += '\n';
 
         Stacktrace stacktrace;
 
-        for(auto& line : stacktrace)
+        // TODO: write this better
+        // (limit the size of stacktrace)
+        uint64_t count = 0;
+
+        for(auto& line : stacktrace) {
             this->message += fmt::format("  at {} ({})\n",
                 line.function, line.location);
+
+            if (++count >= stacktrace_size)
+                break;
+        }
 #endif
     }
 
@@ -33,5 +43,6 @@ public:
 
 private:
     std::string message;
+    uint64_t stacktrace_size;
 };
 
diff --git a/src/communication/bolt/v1/states/error.cpp b/src/communication/bolt/v1/states/error.cpp
index cfcfc9391..27f6bb339 100644
--- a/src/communication/bolt/v1/states/error.cpp
+++ b/src/communication/bolt/v1/states/error.cpp
@@ -3,13 +3,28 @@
 namespace bolt
 {
 
+Error::Error() : State(logging::log->logger("Error State")) {}
+
 State* Error::run(Session& session)
 {
+    logger.trace("Run");
+
+    session.decoder.read_byte();
     auto message_type = session.decoder.read_byte();
 
-    if(message_type == MessageCode::AckFailure)
+    logger.trace("Message type byte is: {:02X}", message_type);
+
+    if (message_type == MessageCode::PullAll)
+    {
+        session.output_stream.write_ignored();
+        session.output_stream.chunk();
+        session.output_stream.send();
+        return this;
+    }
+    else if(message_type == MessageCode::AckFailure)
     {
         // TODO reset current statement? is it even necessary?
+        logger.trace("AckFailure received");
         
         session.output_stream.write_success_empty();
         session.output_stream.chunk();
diff --git a/src/communication/bolt/v1/states/executor.cpp b/src/communication/bolt/v1/states/executor.cpp
index b8e7e9777..fcf25cc30 100644
--- a/src/communication/bolt/v1/states/executor.cpp
+++ b/src/communication/bolt/v1/states/executor.cpp
@@ -8,7 +8,7 @@
 namespace bolt
 {
 
-Executor::Executor() : logger(logging::log->logger("Executor")) {}
+Executor::Executor() : State(logging::log->logger("Executor")) {}
 
 State *Executor::run(Session &session)
 {
@@ -25,15 +25,22 @@ State *Executor::run(Session &session)
 
         q.statement = session.decoder.read_string();
 
-        this->run(session, q);
+        try {
+            return this->run(session, q);
+        } catch (QueryEngineException &e) {
+            session.output_stream.write_failure(
+                {{"code", "Memgraph.QueryEngineException"},
+                 {"message", e.what()}});
+            session.output_stream.send();
+            return session.bolt.states.error.get();
+        }
     } else if (message_type == MessageCode::PullAll) {
         pull_all(session);
     } else if (message_type == MessageCode::DiscardAll) {
         discard_all(session);
     } else if (message_type == MessageCode::Reset) {
-        // todo rollback current transaction
+        // TODO: rollback current transaction
         // discard all records waiting to be sent
-
         return this;
     } else {
         logger.error("Unrecognized message recieved");
@@ -45,20 +52,28 @@ State *Executor::run(Session &session)
     return this;
 }
 
-void Executor::run(Session &session, Query &query)
+State* Executor::run(Session &session, Query &query)
 {
     logger.trace("[Run] '{}'", query.statement);
 
     auto &db = session.active_db();
     logger.debug("[ActiveDB] '{}'", db.name());
 
-    try {
+    auto is_successfully_executed =
         query_engine.execute(query.statement, db, session.output_stream);
-    } catch (QueryEngineException &e) {
+
+    if (!is_successfully_executed) {
         session.output_stream.write_failure(
-            {{"code", "unknown"}, {"message", e.what()}});
+            {{"code", "Memgraph.QueryExecutionFail"},
+             {"message", "Query execution has failed (probably there is no "
+                         "element or there are some problems with concurrent "
+                         "access -> client has to resolve problems with "
+                         "concurrent access)"}});
         session.output_stream.send();
+        return session.bolt.states.error.get();
     }
+
+    return this;
 }
 
 void Executor::pull_all(Session &session)
diff --git a/src/examples/bolt_py_client/initial_test.py b/src/examples/bolt_py_client/initial_test.py
index 358807648..f542944d9 100644
--- a/src/examples/bolt_py_client/initial_test.py
+++ b/src/examples/bolt_py_client/initial_test.py
@@ -21,14 +21,20 @@ queries.append((True, "MATCH (n) WHERE ID(n)=2 RETURN n"))
 queries.append((True, "MATCH (n) WHERE ID(n)=3 RETURN n"))
 queries.append((True, "MATCH (n) WHERE ID(n)=4 RETURN n"))
 queries.append((True, "MATCH (n) WHERE ID(n)=5 RETURN n"))
+queries.append((True, "MATCH (n) RETURN n"));
+queries.append((True, "MATCH (n:PERSON) RETURN n"));
 
 queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=0 AND ID(n2)=1 CREATE (n1)-[r:IS]->(n2) RETURN r"))
 queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=1 AND ID(n2)=2 CREATE (n1)-[r:IS {name: \"test\", age: 23}]->(n2) RETURN r"))
 queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=2 AND ID(n2)=0 CREATE (n1)-[r:IS {name: \"test\", age: 23}]->(n2) RETURN r"))
+queries.append((True, "MATCH (n1), (n2) WHERE ID(n1)=2 AND ID(n2)=0 CREATE (n1)-[r:ARE {name: \"test\", age: 23}]->(n2) RETURN r"))
 
 queries.append((True, "MATCH ()-[r]-() WHERE ID(r)=0 RETURN r"))
 queries.append((True, "MATCH ()-[r]-() WHERE ID(r)=1 RETURN r"))
 queries.append((True, "MATCH ()-[r]-() WHERE ID(r)=2 RETURN r"))
+queries.append((True, "MATCH ()-[r:IS]-() RETURN r"))
+queries.append((True, "MATCH ()-[r:ARE]-() RETURN r"))
+queries.append((True, "MATCH ()-[r]-() RETURN r"))
 
 queries.append((True, "MATCH (n) WHERE ID(n)=1 SET n.name = \"updated_name\" RETURN n"))
 queries.append((True, "MATCH (n) WHERE ID(n)=1 RETURN n"))