diff --git a/.clang-tidy b/.clang-tidy
index 46103a7a9..a60de817b 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,5 +1,6 @@
 ---
 Checks: '*,
+         -altera-struct-pack-align,
          -android-*,
          -cert-err58-cpp,
          -cppcoreguidelines-avoid-c-arrays,
@@ -43,7 +44,6 @@ Checks: '*,
          -llvmlibc-implementation-in-namespace,
          -llvmlibc-restrict-system-libc-headers,
          -misc-non-private-member-variables-in-classes,
-         -misc-unused-parameters,
          -modernize-avoid-c-arrays,
          -modernize-concat-nested-namespaces,
          -modernize-pass-by-value,
diff --git a/include/mg_procedure.h b/include/mg_procedure.h
index db99408c9..93b9b865f 100644
--- a/include/mg_procedure.h
+++ b/include/mg_procedure.h
@@ -7,9 +7,34 @@
 extern "C" {
 #endif
 
+#if __cplusplus >= 201703L
+#define MGP_NODISCARD [[nodiscard]]
+#else
+#define MGP_NODISCARD
+#endif
+
 #include <stddef.h>
 #include <stdint.h>
 
+/// @name Error Codes
+///
+///@{
+
+/// All functions return an error code that can be used to figure out whether the API call was successful or not. In
+/// case of failure, the specific error code can be used to identify the reason of the failure.
+enum MGP_NODISCARD mgp_error {
+  MGP_ERROR_NO_ERROR = 0,
+  MGP_ERROR_UNKNOWN_ERROR,
+  MGP_ERROR_UNABLE_TO_ALLOCATE,
+  MGP_ERROR_INSUFFICIENT_BUFFER,
+  MGP_ERROR_OUT_OF_RANGE,
+  MGP_ERROR_LOGIC_ERROR,
+  MGP_ERROR_NON_EXISTENT_OBJECT,
+  MGP_ERROR_INVALID_ARGUMENT,
+  MGP_ERROR_KEY_ALREADY_EXISTS,
+};
+///@}
+
 /// @name Memory Allocation
 ///
 /// These should be preferred compared to plain malloc calls as Memgraph's
@@ -30,17 +55,17 @@ struct mgp_memory;
 /// Allocate a block of memory with given size in bytes.
 /// Unlike malloc, this function is not thread-safe.
 /// `size_in_bytes` must be greater than 0.
-/// The returned pointer must be freed with mgp_free.
-/// NULL is returned if unable to serve the requested allocation.
-void *mgp_alloc(struct mgp_memory *memory, size_t size_in_bytes);
+/// The resulting pointer must be freed with mgp_free.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to serve the requested allocation.
+enum mgp_error mgp_alloc(struct mgp_memory *memory, size_t size_in_bytes, void **result);
 
 /// Allocate an aligned block of memory with given size in bytes.
 /// Unlike malloc and aligned_alloc, this function is not thread-safe.
 /// `size_in_bytes` must be greater than 0.
 /// `alignment` must be a power of 2 value.
-/// The returned pointer must be freed with mgp_free.
-/// NULL is returned if unable to serve the requested allocation.
-void *mgp_aligned_alloc(struct mgp_memory *memory, size_t size_in_bytes, size_t alignment);
+/// The resulting pointer must be freed with mgp_free.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to serve the requested allocation.
+enum mgp_error mgp_aligned_alloc(struct mgp_memory *memory, size_t size_in_bytes, size_t alignment, void **result);
 
 /// Deallocate an allocation from mgp_alloc or mgp_aligned_alloc.
 /// Unlike free, this function is not thread-safe.
@@ -52,16 +77,16 @@ void mgp_free(struct mgp_memory *memory, void *ptr);
 /// Allocate a global block of memory with given size in bytes.
 /// This function can be used to allocate global memory that persists
 /// beyond a single invocation of mgp_main.
-/// The returned pointer must be freed with mgp_global_free.
-/// NULL is returned if unable to serve the requested allocation.
-void *mgp_global_alloc(size_t size_in_bytes);
+/// The resulting pointer must be freed with mgp_global_free.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to serve the requested allocation.
+enum mgp_error mgp_global_alloc(size_t size_in_bytes, void **result);
 
 /// Allocate an aligned global block of memory with given size in bytes.
 /// This function can be used to allocate global memory that persists
 /// beyond a single invocation of mgp_main.
-/// The returned pointer must be freed with mgp_global_free.
-/// NULL is returned if unable to serve the requested allocation.
-void *mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment);
+/// The resulting pointer must be freed with mgp_global_free.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to serve the requested allocation.
+enum mgp_error mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment, void **result);
 
 /// Deallocate an allocation from mgp_global_alloc or mgp_global_aligned_alloc.
 /// If `ptr` is NULL, this function does nothing.
@@ -117,147 +142,167 @@ void mgp_value_destroy(struct mgp_value *val);
 
 /// Construct a value representing `null` in openCypher.
 /// You need to free the instance through mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_null(struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_null(struct mgp_memory *memory, struct mgp_value **result);
 
 /// Construct a boolean value.
 /// Non-zero values represent `true`, while zero represents `false`.
 /// You need to free the instance through mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_bool(int val, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_bool(int val, struct mgp_memory *memory, struct mgp_value **result);
 
 /// Construct an integer value.
 /// You need to free the instance through mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_int(int64_t val, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_int(int64_t val, struct mgp_memory *memory, struct mgp_value **result);
 
 /// Construct a double floating point value.
 /// You need to free the instance through mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_double(double val, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_double(double val, struct mgp_memory *memory, struct mgp_value **result);
 
 /// Construct a character string value from a NULL terminated string.
 /// You need to free the instance through mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_string(const char *val, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_string(const char *val, struct mgp_memory *memory, struct mgp_value **result);
 
 /// Create a mgp_value storing a mgp_list.
 /// You need to free the instance through mgp_value_destroy. The ownership of
 /// the list is given to the created mgp_value and destroying the mgp_value will
 /// destroy the mgp_list. Therefore, if a mgp_value is successfully created
 /// you must not call mgp_list_destroy on the given list.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_list(struct mgp_list *val);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_list(struct mgp_list *val, struct mgp_value **result);
 
 /// Create a mgp_value storing a mgp_map.
 /// You need to free the instance through mgp_value_destroy. The ownership of
 /// the map is given to the created mgp_value and destroying the mgp_value will
 /// destroy the mgp_map. Therefore, if a mgp_value is successfully created
 /// you must not call mgp_map_destroy on the given map.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_map(struct mgp_map *val);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_map(struct mgp_map *val, struct mgp_value **result);
 
 /// Create a mgp_value storing a mgp_vertex.
 /// You need to free the instance through mgp_value_destroy. The ownership of
 /// the vertex is given to the created mgp_value and destroying the mgp_value
 /// will destroy the mgp_vertex. Therefore, if a mgp_value is successfully
 /// created you must not call mgp_vertex_destroy on the given vertex.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_vertex(struct mgp_vertex *val);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_vertex(struct mgp_vertex *val, struct mgp_value **result);
 
 /// Create a mgp_value storing a mgp_edge.
 /// You need to free the instance through mgp_value_destroy. The ownership of
 /// the edge is given to the created mgp_value and destroying the mgp_value will
 /// destroy the mgp_edge. Therefore, if a mgp_value is successfully created you
 /// must not call mgp_edge_destroy on the given edge.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_edge(struct mgp_edge *val);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_edge(struct mgp_edge *val, struct mgp_value **result);
 
 /// Create a mgp_value storing a mgp_path.
 /// You need to free the instance through mgp_value_destroy. The ownership of
 /// the path is given to the created mgp_value and destroying the mgp_value will
 /// destroy the mgp_path. Therefore, if a mgp_value is successfully created you
 /// must not call mgp_path_destroy on the given path.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_value_make_path(struct mgp_path *val);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_path(struct mgp_path *val, struct mgp_value **result);
 
-/// Return the type of the value contained in mgp_value.
-enum mgp_value_type mgp_value_get_type(const struct mgp_value *val);
+/// Get the type of the value contained in mgp_value.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_type(const struct mgp_value *val, enum mgp_value_type *result);
 
-/// Return non-zero if the given mgp_value represents `null`.
-int mgp_value_is_null(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value represents `null`.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_null(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a boolean.
-int mgp_value_is_bool(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a boolean.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_bool(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores an integer.
-int mgp_value_is_int(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores an integer.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_int(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a double floating-point.
-int mgp_value_is_double(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a double floating-point.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_double(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a character string.
-int mgp_value_is_string(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a character string.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_string(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a list of values.
-int mgp_value_is_list(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a list of values.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_list(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a map of values.
-int mgp_value_is_map(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a map of values.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_map(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a vertex.
-int mgp_value_is_vertex(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a vertex.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_vertex(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores an edge.
-int mgp_value_is_edge(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores an edge.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_edge(const struct mgp_value *val, int *result);
 
-/// Return non-zero if the given mgp_value stores a path.
-int mgp_value_is_path(const struct mgp_value *val);
+/// Result is non-zero if the given mgp_value stores a path.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_path(const struct mgp_value *val, int *result);
 
-/// Return the contained boolean value.
+/// Get the contained boolean value.
 /// Non-zero values represent `true`, while zero represents `false`.
-/// The result is undefined if mgp_value does not contain the expected type.
-int mgp_value_get_bool(const struct mgp_value *val);
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_bool(const struct mgp_value *val, int *result);
 
-/// Return the contained integer.
-/// The result is undefined if mgp_value does not contain the expected type.
-int64_t mgp_value_get_int(const struct mgp_value *val);
+/// Get the contained integer.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_int(const struct mgp_value *val, int64_t *result);
 
-/// Return the contained double floating-point.
-/// The result is undefined if mgp_value does not contain the expected type.
-double mgp_value_get_double(const struct mgp_value *val);
+/// Get the contained double floating-point.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_double(const struct mgp_value *val, double *result);
 
-/// Return the contained character string.
-/// The result is undefined if mgp_value does not contain the expected type.
-const char *mgp_value_get_string(const struct mgp_value *val);
+/// Get the contained character string.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_string(const struct mgp_value *val, const char **result);
 
-/// Return the contained list of values.
-/// The result is undefined if mgp_value does not contain the expected type.
-const struct mgp_list *mgp_value_get_list(const struct mgp_value *val);
+/// Get the contained list of values.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_list(const struct mgp_value *val, const struct mgp_list **result);
 
 /// Return the contained map of values.
-/// The result is undefined if mgp_value does not contain the expected type.
-const struct mgp_map *mgp_value_get_map(const struct mgp_value *val);
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_map(const struct mgp_value *val, const struct mgp_map **result);
 
-/// Return the contained vertex.
-/// The result is undefined if mgp_value does not contain the expected type.
-const struct mgp_vertex *mgp_value_get_vertex(const struct mgp_value *val);
+/// Get the contained vertex.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_vertex(const struct mgp_value *val, const struct mgp_vertex **result);
 
-/// Return the contained edge.
-/// The result is undefined if mgp_value does not contain the expected type.
-const struct mgp_edge *mgp_value_get_edge(const struct mgp_value *val);
+/// Get the contained edge.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_edge(const struct mgp_value *val, const struct mgp_edge **result);
 
-/// Return the contained path.
-/// The result is undefined if mgp_value does not contain the expected type.
-const struct mgp_path *mgp_value_get_path(const struct mgp_value *val);
+/// Get the contained path.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_path(const struct mgp_value *val, const struct mgp_path **result);
 
 /// Create an empty list with given capacity.
 /// You need to free the created instance with mgp_list_destroy.
 /// The created list will have allocated enough memory for `capacity` elements
 /// of mgp_value, but it will not contain any elements. Therefore,
 /// mgp_list_size will return 0.
-/// NULL is returned if unable to allocate a new list.
-struct mgp_list *mgp_list_make_empty(size_t capacity, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_list.
+enum mgp_error mgp_list_make_empty(size_t capacity, struct mgp_memory *memory, struct mgp_list **result);
 
 /// Free the memory used by the given mgp_list and contained elements.
 void mgp_list_destroy(struct mgp_list *list);
@@ -266,9 +311,9 @@ void mgp_list_destroy(struct mgp_list *list);
 /// The list copies the given value and therefore does not take ownership of the
 /// original value. You still need to call mgp_value_destroy to free the
 /// original value.
-/// Return non-zero on success, or 0 if there's no capacity or memory to append
-/// the mgp_value to mgp_list.
-int mgp_list_append(struct mgp_list *list, const struct mgp_value *val);
+/// Return MGP_ERROR_INSUFFICIENT_BUFFER if there's no capacity.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
+enum mgp_error mgp_list_append(struct mgp_list *list, const struct mgp_value *val);
 
 /// Append a copy of mgp_value to mgp_list increasing capacity if needed.
 /// The list copies the given value and therefore does not take ownership of the
@@ -276,25 +321,27 @@ int mgp_list_append(struct mgp_list *list, const struct mgp_value *val);
 /// original value.
 /// In case of a capacity change, the previously contained elements will move in
 /// memory and any references to them will be invalid.
-/// Return non-zero on success, or 0 if there's no memory to append the
-/// mgp_value to mgp_list.
-int mgp_list_append_extend(struct mgp_list *list, const struct mgp_value *val);
+/// Return MGP_ERROR_INSUFFICIENT_BUFFER if there's no capacity.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
+enum mgp_error mgp_list_append_extend(struct mgp_list *list, const struct mgp_value *val);
 
-/// Return the number of elements stored in mgp_list.
-size_t mgp_list_size(const struct mgp_list *list);
+/// Get the number of elements stored in mgp_list.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_list_size(const struct mgp_list *list, size_t *result);
 
-/// Return the total number of elements for which there's already allocated
+/// Get the total number of elements for which there's already allocated
 /// memory in mgp_list.
-size_t mgp_list_capacity(const struct mgp_list *list);
+/// Current implementation always returns without errors.
+enum mgp_error mgp_list_capacity(const struct mgp_list *list, size_t *result);
 
-/// Return the element in mgp_list at given position.
-/// NULL is returned if the index is not within mgp_list_size.
-const struct mgp_value *mgp_list_at(const struct mgp_list *list, size_t index);
+/// Get the element in mgp_list at given position.
+/// MGP_ERROR_OUT_OF_RANGE is returned if the index is not within mgp_list_size.
+enum mgp_error mgp_list_at(const struct mgp_list *list, size_t index, const struct mgp_value **result);
 
 /// Create an empty map of character strings to mgp_value instances.
 /// You need to free the created instance with mgp_map_destroy.
-/// NULL is returned if unable to allocate a new map.
-struct mgp_map *mgp_map_make_empty(struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_map.
+enum mgp_error mgp_map_make_empty(struct mgp_memory *memory, struct mgp_map **result);
 
 /// Free the memory used by the given mgp_map and contained items.
 void mgp_map_destroy(struct mgp_map *map);
@@ -304,34 +351,36 @@ void mgp_map_destroy(struct mgp_map *map);
 /// In case of insertion, both the string and the value are copied into the map.
 /// Therefore, the map does not take ownership of the original key nor value, so
 /// you still need to free their memory explicitly.
-/// Return non-zero on success, or 0 if there's no memory to insert a new
-/// mapping or a previous mapping already exists.
-int mgp_map_insert(struct mgp_map *map, const char *key, const struct mgp_value *value);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate for insertion.
+/// Return MGP_ERROR_KEY_ALREADY_EXISTS if a previous mapping already exists.
+enum mgp_error mgp_map_insert(struct mgp_map *map, const char *key, const struct mgp_value *value);
 
-/// Return the number of items stored in mgp_map.
-size_t mgp_map_size(const struct mgp_map *map);
+/// Get the number of items stored in mgp_map.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_map_size(const struct mgp_map *map, size_t *result);
 
-/// Return the mapped mgp_value to the given character string.
-/// NULL is returned if no mapping exists.
-const struct mgp_value *mgp_map_at(const struct mgp_map *map, const char *key);
+/// Get the mapped mgp_value to the given character string.
+/// Result is NULL if no mapping exists.
+enum mgp_error mgp_map_at(const struct mgp_map *map, const char *key, const struct mgp_value **result);
 
 /// An item in the mgp_map.
 struct mgp_map_item;
 
 /// Get the key of the mapped item.
-const char *mgp_map_item_key(const struct mgp_map_item *item);
+enum mgp_error mgp_map_item_key(const struct mgp_map_item *item, const char **result);
 
 /// Get the value of the mapped item.
-const struct mgp_value *mgp_map_item_value(const struct mgp_map_item *item);
+enum mgp_error mgp_map_item_value(const struct mgp_map_item *item, const struct mgp_value **result);
 
 /// An iterator over the items in mgp_map.
 struct mgp_map_items_iterator;
 
 /// Start iterating over items contained in the given map.
-/// The returned mgp_map_items_iterator needs to be deallocated with
+/// The resulting mgp_map_items_iterator needs to be deallocated with
 /// mgp_map_items_iterator_destroy.
-/// NULL is returned if unable to allocate a new iterator.
-struct mgp_map_items_iterator *mgp_map_iter_items(const struct mgp_map *map, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_map_items_iterator.
+enum mgp_error mgp_map_iter_items(const struct mgp_map *map, struct mgp_memory *memory,
+                                  struct mgp_map_items_iterator **result);
 
 /// Deallocate memory used by mgp_map_items_iterator.
 void mgp_map_items_iterator_destroy(struct mgp_map_items_iterator *it);
@@ -343,24 +392,25 @@ void mgp_map_items_iterator_destroy(struct mgp_map_items_iterator *it);
 /// throughout the lifetime of a map. Therefore, you can store the key as well
 /// as the value before, and use them after invoking
 /// mgp_map_items_iterator_next.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_map_item *mgp_map_items_iterator_get(const struct mgp_map_items_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_map_items_iterator_get(const struct mgp_map_items_iterator *it, const struct mgp_map_item **result);
 
 /// Advance the iterator to the next item stored in map and return it.
 /// The previous pointer obtained through mgp_map_items_iterator_get will
 /// be invalidated, but the pointers to key and value will remain valid.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_map_item *mgp_map_items_iterator_next(struct mgp_map_items_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_map_items_iterator_next(struct mgp_map_items_iterator *it, const struct mgp_map_item **result);
 
 /// Create a path with the copy of the given starting vertex.
 /// You need to free the created instance with mgp_path_destroy.
-/// NULL is returned if unable to allocate a path.
-struct mgp_path *mgp_path_make_with_start(const struct mgp_vertex *vertex, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_path.
+enum mgp_error mgp_path_make_with_start(const struct mgp_vertex *vertex, struct mgp_memory *memory,
+                                        struct mgp_path **result);
 
 /// Copy a mgp_path.
 /// Returned pointer must be freed with mgp_path_destroy.
-/// NULL is returned if unable to allocate a mgp_path.
-struct mgp_path *mgp_path_copy(const struct mgp_path *path, struct mgp_memory *memory);
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_path.
+enum mgp_error mgp_path_copy(const struct mgp_path *path, struct mgp_memory *memory, struct mgp_path **result);
 
 /// Free the memory used by the given mgp_path and contained vertices and edges.
 void mgp_path_destroy(struct mgp_path *path);
@@ -371,26 +421,26 @@ void mgp_path_destroy(struct mgp_path *path);
 /// explicitly.
 /// The last vertex on the path will become the other endpoint of the given
 /// edge, as continued from the current last vertex.
-/// Return non-zero on success, or 0 if the current last vertex in the path is
-/// not part of the given edge. 0 is also returned if unable to allocate memory
-/// for path extension.
-int mgp_path_expand(struct mgp_path *path, const struct mgp_edge *edge);
+/// Return MGP_ERROR_LOGIC_ERROR if the current last vertex in the path is not part of the given edge.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for path extension.
+enum mgp_error mgp_path_expand(struct mgp_path *path, const struct mgp_edge *edge);
 
-/// Return the number of edges in a mgp_path.
-size_t mgp_path_size(const struct mgp_path *path);
+/// Get the number of edges in a mgp_path.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_path_size(const struct mgp_path *path, size_t *result);
 
-/// Return the vertex from a path at given index.
+/// Get the vertex from a path at given index.
 /// The valid index range is [0, mgp_path_size].
-/// NULL is returned if index is out of range.
-const struct mgp_vertex *mgp_path_vertex_at(const struct mgp_path *path, size_t index);
+/// MGP_ERROR_OUT_OF_RANGE is returned if index is out of range.
+enum mgp_error mgp_path_vertex_at(const struct mgp_path *path, size_t index, const struct mgp_vertex **result);
 
-/// Return the edge from a path at given index.
+/// Get the edge from a path at given index.
 /// The valid index range is [0, mgp_path_size - 1].
-/// NULL is returned if index is out of range.
-const struct mgp_edge *mgp_path_edge_at(const struct mgp_path *path, size_t index);
+/// MGP_ERROR_OUT_OF_RANGE is returned if index is out of range.
+enum mgp_error mgp_path_edge_at(const struct mgp_path *path, size_t index, const struct mgp_edge **result);
 
-/// Return non-zero if given paths are equal, otherwise 0.
-int mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2);
+/// Result is non-zero if given paths are equal, otherwise 0.
+enum mgp_error mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2, int *result);
 
 ///@}
 
@@ -405,20 +455,20 @@ struct mgp_result;
 struct mgp_result_record;
 
 /// Set the error as the result of the procedure.
-/// If there's no memory for copying the error message, 0 is returned.
-int mgp_result_set_error_msg(struct mgp_result *res, const char *error_msg);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE ff there's no memory for copying the error message.
+enum mgp_error mgp_result_set_error_msg(struct mgp_result *res, const char *error_msg);
 
 /// Create a new record for results.
-/// The previously returned pointer to mgp_result_record is no longer valid, and
-/// you must not use it.
-/// Return NULL if unable to allocate a mgp_result_record.
-struct mgp_result_record *mgp_result_new_record(struct mgp_result *res);
+/// The previously obtained mgp_result_record pointer is no longer valid, and you must not use it.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_result_record.
+enum mgp_error mgp_result_new_record(struct mgp_result *res, struct mgp_result_record **result);
 
 /// Assign a value to a field in the given record.
-/// Return 0 if there's no memory to copy the mgp_value to mgp_result_record or
-/// if the combination of `field_name` and `val` does not satisfy the
-/// procedure's result signature.
-int mgp_result_record_insert(struct mgp_result_record *record, const char *field_name, const struct mgp_value *val);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory to copy the mgp_value to mgp_result_record.
+/// Return MGP_ERROR_OUT_OF_RANGE if there is no field named `field_name`.
+/// Return MGP_ERROR_LOGIC_ERROR `val` does not satisfy the type of the field name `field_name`.
+enum mgp_error mgp_result_record_insert(struct mgp_result_record *record, const char *field_name,
+                                        const struct mgp_value *val);
 ///@}
 
 /// @name Graph Constructs
@@ -453,14 +503,15 @@ struct mgp_property {
 /// Get the current property pointed to by the iterator.
 /// When the mgp_properties_iterator_next is invoked, the previous
 /// mgp_property is invalidated and its value must not be used.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_property *mgp_properties_iterator_get(const struct mgp_properties_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_properties_iterator_get(const struct mgp_properties_iterator *it,
+                                           const struct mgp_property **result);
 
 /// Advance the iterator to the next property and return it.
 /// The previous mgp_property obtained through mgp_properties_iterator_get
 /// will be invalidated, and you must not use its value.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_property *mgp_properties_iterator_next(struct mgp_properties_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_properties_iterator_next(struct mgp_properties_iterator *it, const struct mgp_property **result);
 
 /// Iterator over edges of a vertex.
 struct mgp_edges_iterator;
@@ -476,67 +527,70 @@ struct mgp_vertex_id {
 /// Get the ID of given vertex.
 /// The ID is only valid for a single query execution, you should never store it
 /// globally in a query module.
-struct mgp_vertex_id mgp_vertex_get_id(const struct mgp_vertex *v);
+enum mgp_error mgp_vertex_get_id(const struct mgp_vertex *v, struct mgp_vertex_id *result);
 
 /// Copy a mgp_vertex.
-/// Returned pointer must be freed with mgp_vertex_destroy.
-/// NULL is returned if unable to allocate a mgp_vertex.
-struct mgp_vertex *mgp_vertex_copy(const struct mgp_vertex *v, struct mgp_memory *memory);
+/// Resulting pointer must be freed with mgp_vertex_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_vertex.
+enum mgp_error mgp_vertex_copy(const struct mgp_vertex *v, struct mgp_memory *memory, struct mgp_vertex **result);
 
 /// Free the memory used by a mgp_vertex.
 void mgp_vertex_destroy(struct mgp_vertex *v);
 
-/// Return non-zero if given vertices are equal, otherwise 0.
-int mgp_vertex_equal(const struct mgp_vertex *v1, const struct mgp_vertex *v2);
+/// Result is non-zero if given vertices are equal, otherwise 0.
+enum mgp_error mgp_vertex_equal(const struct mgp_vertex *v1, const struct mgp_vertex *v2, int *result);
 
-/// Return the number of labels a given vertex has.
-size_t mgp_vertex_labels_count(const struct mgp_vertex *v);
+/// Get the number of labels a given vertex has.
+enum mgp_error mgp_vertex_labels_count(const struct mgp_vertex *v, size_t *result);
 
-/// Return mgp_label in mgp_vertex at given index.
-/// If the index is out of bounds, mgp_label.name is set to NULL.
-struct mgp_label mgp_vertex_label_at(const struct mgp_vertex *v, size_t index);
+/// Get mgp_label in mgp_vertex at given index.
+/// Return MGP_ERROR_OUT_OF_RANGE if the index is out of range.
+enum mgp_error mgp_vertex_label_at(const struct mgp_vertex *v, size_t index, struct mgp_label *result);
 
-/// Return non-zero if the given vertex has the given label.
-int mgp_vertex_has_label(const struct mgp_vertex *v, struct mgp_label label);
+/// Result is non-zero if the given vertex has the given label.
+enum mgp_error mgp_vertex_has_label(const struct mgp_vertex *v, struct mgp_label label, int *result);
 
-/// Return non-zero if the given vertex has a label with given name.
-int mgp_vertex_has_label_named(const struct mgp_vertex *v, const char *label_name);
+/// Result is non-zero if the given vertex has a label with given name.
+enum mgp_error mgp_vertex_has_label_named(const struct mgp_vertex *v, const char *label_name, int *result);
 
 /// Get a copy of a vertex property mapped to a given name.
-/// Returned value must be freed with mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_vertex_get_property(const struct mgp_vertex *v, const char *property_name,
-                                          struct mgp_memory *memory);
+/// Resulting value must be freed with mgp_value_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
+enum mgp_error mgp_vertex_get_property(const struct mgp_vertex *v, const char *property_name, struct mgp_memory *memory,
+                                       struct mgp_value **result);
 
 /// Start iterating over properties stored in the given vertex.
-/// The returned mgp_properties_iterator needs to be deallocated with
+/// The resulting mgp_properties_iterator needs to be deallocated with
 /// mgp_properties_iterator_destroy.
-/// NULL is returned if unable to allocate a new iterator.
-struct mgp_properties_iterator *mgp_vertex_iter_properties(const struct mgp_vertex *v, struct mgp_memory *memory);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_properties_iterator.
+enum mgp_error mgp_vertex_iter_properties(const struct mgp_vertex *v, struct mgp_memory *memory,
+                                          struct mgp_properties_iterator **result);
 
 /// Start iterating over inbound edges of the given vertex.
-/// The returned mgp_edges_iterator needs to be deallocated with
+/// The resulting mgp_edges_iterator needs to be deallocated with
 /// mgp_edges_iterator_destroy.
-/// NULL is returned if unable to allocate a new iterator.
-struct mgp_edges_iterator *mgp_vertex_iter_in_edges(const struct mgp_vertex *v, struct mgp_memory *memory);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edges_iterator.
+enum mgp_error mgp_vertex_iter_in_edges(const struct mgp_vertex *v, struct mgp_memory *memory,
+                                        struct mgp_edges_iterator **result);
 
 /// Start iterating over outbound edges of the given vertex.
 /// The returned mgp_edges_iterator needs to be deallocated with
 /// mgp_edges_iterator_destroy.
-/// NULL is returned if unable to allocate a new iterator.
-struct mgp_edges_iterator *mgp_vertex_iter_out_edges(const struct mgp_vertex *v, struct mgp_memory *memory);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edges_iterator.
+enum mgp_error mgp_vertex_iter_out_edges(const struct mgp_vertex *v, struct mgp_memory *memory,
+                                         struct mgp_edges_iterator **result);
 
 /// Get the current edge pointed to by the iterator.
 /// When the mgp_edges_iterator_next is invoked, the previous
 /// mgp_edge is invalidated and its value must not be used.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_edge *mgp_edges_iterator_get(const struct mgp_edges_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_edges_iterator_get(const struct mgp_edges_iterator *it, const struct mgp_edge **result);
 
 /// Advance the iterator to the next edge and return it.
 /// The previous mgp_edge obtained through mgp_edges_iterator_get
 /// will be invalidated, and you must not use its value.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_edge *mgp_edges_iterator_next(struct mgp_edges_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_edges_iterator_next(struct mgp_edges_iterator *it, const struct mgp_edge **result);
 
 /// ID of an edge; valid during a single query execution.
 struct mgp_edge_id {
@@ -546,47 +600,51 @@ struct mgp_edge_id {
 /// Get the ID of given edge.
 /// The ID is only valid for a single query execution, you should never store it
 /// globally in a query module.
-struct mgp_edge_id mgp_edge_get_id(const struct mgp_edge *e);
+enum mgp_error mgp_edge_get_id(const struct mgp_edge *e, struct mgp_edge_id *result);
 
 /// Copy a mgp_edge.
-/// Returned pointer must be freed with mgp_edge_destroy.
-/// NULL is returned if unable to allocate a mgp_edge.
-struct mgp_edge *mgp_edge_copy(const struct mgp_edge *e, struct mgp_memory *memory);
+/// Resulting pointer must be freed with mgp_edge_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edge.
+enum mgp_error mgp_edge_copy(const struct mgp_edge *e, struct mgp_memory *memory, struct mgp_edge **result);
 
 /// Free the memory used by a mgp_edge.
 void mgp_edge_destroy(struct mgp_edge *e);
 
-/// Return non-zero if given edges are equal, otherwise 0.
-int mgp_edge_equal(const struct mgp_edge *e1, const struct mgp_edge *e2);
+/// Result is non-zero if given edges are equal, otherwise 0.
+enum mgp_error mgp_edge_equal(const struct mgp_edge *e1, const struct mgp_edge *e2, int *result);
 
-/// Return the type of the given edge.
-struct mgp_edge_type mgp_edge_get_type(const struct mgp_edge *e);
+/// Get the type of the given edge.
+enum mgp_error mgp_edge_get_type(const struct mgp_edge *e, struct mgp_edge_type *result);
 
-/// Return the source vertex of the given edge.
-const struct mgp_vertex *mgp_edge_get_from(const struct mgp_edge *e);
+/// Get the source vertex of the given edge.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_edge_get_from(const struct mgp_edge *e, const struct mgp_vertex **result);
 
-/// Return the destination vertex of the given edge.
-const struct mgp_vertex *mgp_edge_get_to(const struct mgp_edge *e);
+/// Get the destination vertex of the given edge.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_edge_get_to(const struct mgp_edge *e, const struct mgp_vertex **result);
 
 /// Get a copy of a edge property mapped to a given name.
-/// Returned value must be freed with mgp_value_destroy.
-/// NULL is returned if unable to allocate a mgp_value.
-struct mgp_value *mgp_edge_get_property(const struct mgp_edge *e, const char *property_name, struct mgp_memory *memory);
+/// Resulting value must be freed with mgp_value_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
+enum mgp_error mgp_edge_get_property(const struct mgp_edge *e, const char *property_name, struct mgp_memory *memory,
+                                     struct mgp_value **result);
 
 /// Start iterating over properties stored in the given edge.
-/// The returned mgp_properties_iterator needs to be deallocated with
+/// Resulting mgp_properties_iterator needs to be deallocated with
 /// mgp_properties_iterator_destroy.
-/// NULL is returned if unable to allocate a new iterator.
-struct mgp_properties_iterator *mgp_edge_iter_properties(const struct mgp_edge *e, struct mgp_memory *memory);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_properties_iterator.
+enum mgp_error mgp_edge_iter_properties(const struct mgp_edge *e, struct mgp_memory *memory,
+                                        struct mgp_properties_iterator **result);
 
 /// State of the graph database.
 struct mgp_graph;
 
 /// Return the vertex corresponding to given ID.
-/// The returned vertex must be freed using mgp_vertex_destroy.
-/// NULL is returned if unable to allocate the vertex or if ID is not valid.
-struct mgp_vertex *mgp_graph_get_vertex_by_id(const struct mgp_graph *g, struct mgp_vertex_id id,
-                                              struct mgp_memory *memory);
+/// Resulting vertex must be freed using mgp_vertex_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the vertex or if ID is not valid.
+enum mgp_error mgp_graph_get_vertex_by_id(const struct mgp_graph *g, struct mgp_vertex_id id, struct mgp_memory *memory,
+                                          struct mgp_vertex **result);
 
 /// Iterator over vertices.
 struct mgp_vertices_iterator;
@@ -595,22 +653,22 @@ struct mgp_vertices_iterator;
 void mgp_vertices_iterator_destroy(struct mgp_vertices_iterator *it);
 
 /// Start iterating over vertices of the given graph.
-/// The returned mgp_vertices_iterator needs to be deallocated with
-/// mgp_vertices_iterator_destroy.
-/// NULL is returned if unable to allocate a new iterator.
-struct mgp_vertices_iterator *mgp_graph_iter_vertices(const struct mgp_graph *g, struct mgp_memory *memory);
+/// Resulting mgp_vertices_iterator needs to be deallocated with mgp_vertices_iterator_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_vertices_iterator.
+enum mgp_error mgp_graph_iter_vertices(const struct mgp_graph *g, struct mgp_memory *memory,
+                                       struct mgp_vertices_iterator **result);
 
 /// Get the current vertex pointed to by the iterator.
 /// When the mgp_vertices_iterator_next is invoked, the previous
 /// mgp_vertex is invalidated and its value must not be used.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_vertex *mgp_vertices_iterator_get(const struct mgp_vertices_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_vertices_iterator_get(const struct mgp_vertices_iterator *it, const struct mgp_vertex **result);
 
 /// Advance the iterator to the next vertex and return it.
 /// The previous mgp_vertex obtained through mgp_vertices_iterator_get
 /// will be invalidated, and you must not use its value.
-/// NULL is returned if the end of the iteration has been reached.
-const struct mgp_vertex *mgp_vertices_iterator_next(struct mgp_vertices_iterator *it);
+/// Result is NULL if the end of the iteration has been reached.
+enum mgp_error mgp_vertices_iterator_next(struct mgp_vertices_iterator *it, const struct mgp_vertex **result);
 ///@}
 
 /// @name Type System
@@ -628,24 +686,30 @@ struct mgp_type;
 /// Get the type representing any value that isn't `null`.
 ///
 /// The ANY type is the parent type of all types.
-const struct mgp_type *mgp_type_any();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_any(const struct mgp_type **result);
 
 /// Get the type representing boolean values.
-const struct mgp_type *mgp_type_bool();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_bool(const struct mgp_type **result);
 
 /// Get the type representing character string values.
-const struct mgp_type *mgp_type_string();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_string(const struct mgp_type **result);
 
 /// Get the type representing integer values.
-const struct mgp_type *mgp_type_int();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_int(const struct mgp_type **result);
 
 /// Get the type representing floating-point values.
-const struct mgp_type *mgp_type_float();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_float(const struct mgp_type **result);
 
 /// Get the type representing any number value.
 ///
 /// This is the parent type for numeric types, i.e. INTEGER and FLOAT.
-const struct mgp_type *mgp_type_number();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_number(const struct mgp_type **result);
 
 /// Get the type representing map values.
 ///
@@ -656,32 +720,36 @@ const struct mgp_type *mgp_type_number();
 ///
 /// @sa mgp_type_node
 /// @sa mgp_type_relationship
-const struct mgp_type *mgp_type_map();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_map(const struct mgp_type **result);
 
 /// Get the type representing graph node values.
 ///
 /// Since a node contains a map of properties, the node itself is also of MAP
 /// type.
-const struct mgp_type *mgp_type_node();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_node(const struct mgp_type **result);
 
 /// Get the type representing graph relationship values.
 ///
 /// Since a relationship contains a map of properties, the relationship itself
 /// is also of MAP type.
-const struct mgp_type *mgp_type_relationship();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_relationship(const struct mgp_type **result);
 
 /// Get the type representing a graph path (walk) from one node to another.
-const struct mgp_type *mgp_type_path();
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_path(const struct mgp_type **result);
 
 /// Build a type representing a list of values of given `element_type`.
 ///
-/// NULL is returned if unable to allocate the new type.
-const struct mgp_type *mgp_type_list(const struct mgp_type *element_type);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_list(const struct mgp_type *element_type, const struct mgp_type **result);
 
 /// Build a type representing either a `null` value or a value of given `type`.
 ///
-/// NULL is returned if unable to allocate the new type.
-const struct mgp_type *mgp_type_nullable(const struct mgp_type *type);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_nullable(const struct mgp_type *type, const struct mgp_type **result);
 ///@}
 
 /// @name Query Module & Procedures
@@ -719,9 +787,11 @@ typedef void (*mgp_proc_cb)(const struct mgp_list *, const struct mgp_graph *, s
 /// Note that Unicode characters are not allowed. Additionally, names are
 /// case-sensitive.
 ///
-/// NULL is returned if unable to allocate memory for mgp_proc; if `name` is
-/// not valid or a procedure with the same name was already registered.
-struct mgp_proc *mgp_module_add_read_procedure(struct mgp_module *module, const char *name, mgp_proc_cb cb);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for mgp_proc.
+/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid procedure name.
+/// RETURN MGP_ERROR_LOGIC_ERROR if a procedure with the same name was already registered.
+enum mgp_error mgp_module_add_read_procedure(struct mgp_module *module, const char *name, mgp_proc_cb cb,
+                                             struct mgp_proc **result);
 
 /// Add a required argument to a procedure.
 ///
@@ -734,10 +804,10 @@ struct mgp_proc *mgp_module_add_read_procedure(struct mgp_module *module, const
 ///
 /// Passed in `type` describes what kind of values can be used as the argument.
 ///
-/// 0 is returned if unable to allocate memory for an argument; if invoking this
-/// function after setting an optional argument or if `name` is not valid.
-/// Non-zero is returned on success.
-int mgp_proc_add_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
+/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid argument name.
+/// RETURN MGP_ERROR_LOGIC_ERROR if the procedure already has any optional argument.
+enum mgp_error mgp_proc_add_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
 
 /// Add an optional argument with a default value to a procedure.
 ///
@@ -757,11 +827,12 @@ int mgp_proc_add_arg(struct mgp_proc *proc, const char *name, const struct mgp_t
 /// a graph element (node, relationship, path) and it must satisfy the given
 /// `type`.
 ///
-/// 0 is returned if unable to allocate memory for an argument; if `name` is
-/// not valid or `default_value` does not satisfy `type`. Non-zero is returned
-/// on success.
-int mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type,
-                         const struct mgp_value *default_value);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
+/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid argument name.
+/// RETURN MGP_ERROR_OUT_OF_RANGE if `default_value` is a graph element (vertex, edge or path).
+/// RETURN MGP_ERROR_LOGIC_ERROR if `default_value` does not satisfy `type`.
+enum mgp_error mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type,
+                                    const struct mgp_value *default_value);
 
 /// Add a result field to a procedure.
 ///
@@ -771,16 +842,20 @@ int mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, const struct m
 /// Passed in `type` describes what kind of values can be returned through the
 /// result field.
 ///
-/// 0 is returned if unable to allocate memory for a result field; if
-/// `name` is not valid or if a result field with the same name was already
-/// added. Non-zero is returned on success.
-int mgp_proc_add_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
+/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid result name.
+/// RETURN MGP_ERROR_LOGIC_ERROR if a result field with the same name was already added.
+enum mgp_error mgp_proc_add_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
 
 /// Add a result field to a procedure and mark it as deprecated.
 ///
 /// This is the same as mgp_proc_add_result, but the result field will be marked
 /// as deprecated.
-int mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
+///
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
+/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid result name.
+/// RETURN MGP_ERROR_LOGIC_ERROR if a result field with the same name was already added.
+enum mgp_error mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
 ///@}
 
 /// @name Execution
@@ -817,28 +892,29 @@ struct mgp_messages;
 /// Payload is not null terminated and not a string but rather a byte array.
 /// You need to call mgp_message_payload_size() first, to read the size of
 /// the payload.
-const char *mgp_message_payload(const struct mgp_message *);
+enum mgp_error mgp_message_payload(const struct mgp_message *message, const char **result);
 
 /// Return the payload size
-size_t mgp_message_payload_size(const struct mgp_message *);
+enum mgp_error mgp_message_payload_size(const struct mgp_message *message, size_t *result);
 
 /// Return the name of topic
-const char *mgp_message_topic_name(const struct mgp_message *);
+enum mgp_error mgp_message_topic_name(const struct mgp_message *message, const char **result);
 
 /// Return the key of mgp_message as a byte array
-const char *mgp_message_key(const struct mgp_message *);
+enum mgp_error mgp_message_key(const struct mgp_message *message, const char **result);
 
 /// Return the key size of mgp_message
-size_t mgp_message_key_size(const struct mgp_message *);
+enum mgp_error mgp_message_key_size(const struct mgp_message *message, size_t *result);
 
 /// Return the timestamp of mgp_message as a byte array
-int64_t mgp_message_timestamp(const struct mgp_message *);
+enum mgp_error mgp_message_timestamp(const struct mgp_message *message, int64_t *result);
 
 /// Return the number of messages contained in the mgp_messages list
-size_t mgp_messages_size(const struct mgp_messages *);
+/// Current implementation always returns without errors.
+enum mgp_error mgp_messages_size(const struct mgp_messages *message, size_t *result);
 
 /// Return the message from a messages list at given index
-const struct mgp_message *mgp_messages_at(const struct mgp_messages *, size_t);
+enum mgp_error mgp_messages_at(const struct mgp_messages *message, size_t index, const struct mgp_message **result);
 
 /// Entry-point for a module transformation, invoked through a stream transformation.
 ///
@@ -848,9 +924,17 @@ const struct mgp_message *mgp_messages_at(const struct mgp_messages *, size_t);
 typedef void (*mgp_trans_cb)(const struct mgp_messages *, const struct mgp_graph *, struct mgp_result *,
                              struct mgp_memory *);
 
-/// Adds a transformation cb to the module pointed by mgp_module.
-/// Return non-zero if the transformation is added successfully.
-int mgp_module_add_transformation(struct mgp_module *module, const char *name, mgp_trans_cb cb);
+/// Register a transformation with a module.
+///
+/// The `name` must be a sequence of digits, underscores, lowercase and
+/// uppercase Latin letters. The name must begin with a non-digit character.
+/// Note that Unicode characters are not allowed. Additionally, names are
+/// case-sensitive.
+///
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for transformation.
+/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid transformation name.
+/// RETURN MGP_ERROR_LOGIC_ERROR if a transformation with the same name was already registered.
+enum mgp_error mgp_module_add_transformation(struct mgp_module *module, const char *name, mgp_trans_cb cb);
 /// @}
 
 #ifdef __cplusplus
diff --git a/query_modules/example.c b/query_modules/example.c
index 463feb6c0..c64a44a5f 100644
--- a/query_modules/example.c
+++ b/query_modules/example.c
@@ -16,61 +16,101 @@
 //   CALL example.procedure(1, 2) YIELD args, result;
 //   CALL example.procedure(1) YIELD args, result;
 // Naturally, you may pass in different arguments or yield less fields.
-static void procedure(const struct mgp_list *args,
-                      const struct mgp_graph *graph, struct mgp_result *result,
+static void procedure(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
                       struct mgp_memory *memory) {
-  struct mgp_list *args_copy = mgp_list_make_empty(mgp_list_size(args), memory);
-  if (args_copy == NULL) goto error_memory;
-  for (size_t i = 0; i < mgp_list_size(args); ++i) {
-    int success = mgp_list_append(args_copy, mgp_list_at(args, i));
-    if (!success) goto error_free_list;
+  size_t args_size = 0;
+  if (mgp_list_size(args, &args_size) != MGP_ERROR_NO_ERROR) {
+    goto error_something_went_wrong;
+  }
+  struct mgp_list *args_copy = NULL;
+  if (mgp_list_make_empty(args_size, memory, &args_copy) != MGP_ERROR_NO_ERROR) {
+    goto error_something_went_wrong;
+  }
+  for (size_t i = 0; i < args_size; ++i) {
+    const struct mgp_value *value = NULL;
+    if (mgp_list_at(args, i, &value) != MGP_ERROR_NO_ERROR) {
+      goto error_free_list;
+    }
+    if (mgp_list_append(args_copy, value) != MGP_ERROR_NO_ERROR) {
+      goto error_free_list;
+    }
+  }
+  struct mgp_result_record *record = NULL;
+  if (mgp_result_new_record(result, &record) != MGP_ERROR_NO_ERROR) {
+    goto error_free_list;
   }
-  struct mgp_result_record *record = mgp_result_new_record(result);
-  if (record == NULL) goto error_free_list;
   // Transfer ownership of args_copy to mgp_value.
-  struct mgp_value *args_value = mgp_value_make_list(args_copy);
-  if (args_value == NULL) goto error_free_list;
-  int args_inserted = mgp_result_record_insert(record, "args", args_value);
+  struct mgp_value *args_value = NULL;
+  if (mgp_value_make_list(args_copy, &args_value) != MGP_ERROR_NO_ERROR) {
+    goto error_free_list;
+  }
   // Release `args_value` and contained `args_copy`.
+  if (mgp_result_record_insert(record, "args", args_value) != MGP_ERROR_NO_ERROR) {
+    mgp_value_destroy(args_value);
+    goto error_something_went_wrong;
+  }
   mgp_value_destroy(args_value);
-  if (!args_inserted) goto error_memory;
-  struct mgp_value *hello_world_value =
-      mgp_value_make_string("Hello World!", memory);
-  if (hello_world_value == NULL) goto error_memory;
-  int result_inserted =
-      mgp_result_record_insert(record, "result", hello_world_value);
+  struct mgp_value *hello_world_value = NULL;
+  if (mgp_value_make_string("Hello World!", memory, &hello_world_value) != MGP_ERROR_NO_ERROR) {
+    goto error_something_went_wrong;
+  }
+  enum mgp_error insert_result = mgp_result_record_insert(record, "result", hello_world_value);
   mgp_value_destroy(hello_world_value);
-  if (!result_inserted) goto error_memory;
+  if (insert_result != MGP_ERROR_NO_ERROR) {
+    goto error_something_went_wrong;
+  }
   // We have successfully finished, so return without error reporting.
   return;
 
 error_free_list:
   mgp_list_destroy(args_copy);
-error_memory:
-  mgp_result_set_error_msg(result, "Not enough memory!");
+error_something_went_wrong:
+  mgp_result_set_error_msg(result, "Something went wrong!");
   return;
 }
 
 // Each module needs to define mgp_init_module function.
 // Here you can register multiple procedures your module supports.
 int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
-  struct mgp_proc *proc =
-      mgp_module_add_read_procedure(module, "procedure", procedure);
-  if (!proc) return 1;
-  if (!mgp_proc_add_arg(proc, "required_arg",
-                        mgp_type_nullable(mgp_type_any())))
+  struct mgp_proc *proc = NULL;
+  if (mgp_module_add_read_procedure(module, "procedure", procedure, &proc) != MGP_ERROR_NO_ERROR) {
     return 1;
-  struct mgp_value *null_value = mgp_value_make_null(memory);
-  if (!mgp_proc_add_opt_arg(proc, "optional_arg",
-                            mgp_type_nullable(mgp_type_any()), null_value)) {
+  }
+  const struct mgp_type *any_type = NULL;
+  if (mgp_type_any(&any_type) != MGP_ERROR_NO_ERROR) {
+    return 1;
+  }
+  const struct mgp_type *nullable_any_type = NULL;
+  if (mgp_type_nullable(any_type, &nullable_any_type) != MGP_ERROR_NO_ERROR) {
+    return 1;
+  }
+  if (mgp_proc_add_arg(proc, "required_arg", nullable_any_type) != MGP_ERROR_NO_ERROR) {
+    return 1;
+  }
+
+  struct mgp_value *null_value = NULL;
+  if (mgp_value_make_null(memory, &null_value) != MGP_ERROR_NO_ERROR) {
+    return 1;
+  }
+  if (mgp_proc_add_opt_arg(proc, "optional_arg", nullable_any_type, null_value) != MGP_ERROR_NO_ERROR) {
     mgp_value_destroy(null_value);
     return 1;
   }
   mgp_value_destroy(null_value);
-  if (!mgp_proc_add_result(proc, "result", mgp_type_string())) return 1;
-  if (!mgp_proc_add_result(proc, "args",
-                           mgp_type_list(mgp_type_nullable(mgp_type_any()))))
+  const struct mgp_type *string = NULL;
+  if (mgp_type_string(&string) != MGP_ERROR_NO_ERROR) {
     return 1;
+  }
+  if (mgp_proc_add_result(proc, "result", string) != MGP_ERROR_NO_ERROR) {
+    return 1;
+  }
+  const struct mgp_type *list_of_anything = NULL;
+  if (mgp_type_list(nullable_any_type, &list_of_anything) != MGP_ERROR_NO_ERROR) {
+    return 1;
+  }
+  if (mgp_proc_add_result(proc, "args", list_of_anything)) {
+    return 1;
+  }
   return 0;
 }
 
diff --git a/src/integrations/kafka/consumer.cpp b/src/integrations/kafka/consumer.cpp
index af073633b..08c4f90c2 100644
--- a/src/integrations/kafka/consumer.cpp
+++ b/src/integrations/kafka/consumer.cpp
@@ -229,13 +229,13 @@ void Consumer::Check(std::optional<std::chrono::milliseconds> timeout, std::opti
   utils::OnScopeExit restore_is_running([this] { is_running_.store(false); });
 
   if (last_assignment_.empty()) {
-    if (auto err = consumer_->assignment(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
+    if (const auto err = consumer_->assignment(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
       spdlog::warn("Saving the commited offset of consumer {} failed: {}", info_.consumer_name, RdKafka::err2str(err));
       throw ConsumerCheckFailedException(info_.consumer_name,
                                          fmt::format("Couldn't save commited offsets: '{}'", RdKafka::err2str(err)));
     }
   } else {
-    if (auto err = consumer_->assign(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
+    if (const auto err = consumer_->assign(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
       throw ConsumerCheckFailedException(info_.consumer_name,
                                          fmt::format("Couldn't restore commited offsets: '{}'", RdKafka::err2str(err)));
     }
@@ -300,7 +300,7 @@ void Consumer::StartConsuming() {
   is_running_.store(true);
 
   if (!last_assignment_.empty()) {
-    if (auto err = consumer_->assign(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
+    if (const auto err = consumer_->assign(last_assignment_); err != RdKafka::ERR_NO_ERROR) {
       throw ConsumerStartFailedException(info_.consumer_name,
                                          fmt::format("Couldn't restore commited offsets: '{}'", RdKafka::err2str(err)));
     }
@@ -328,7 +328,7 @@ void Consumer::StartConsuming() {
 
       try {
         consumer_function_(batch);
-        if (auto err = consumer_->commitSync(); err != RdKafka::ERR_NO_ERROR) {
+        if (const auto err = consumer_->commitSync(); err != RdKafka::ERR_NO_ERROR) {
           spdlog::warn("Committing offset of consumer {} failed: {}", info_.consumer_name, RdKafka::err2str(err));
           break;
         }
diff --git a/src/query/context.hpp b/src/query/context.hpp
index afdd83f5d..5245bdbaf 100644
--- a/src/query/context.hpp
+++ b/src/query/context.hpp
@@ -64,7 +64,7 @@ struct ExecutionContext {
 static_assert(std::is_move_assignable_v<ExecutionContext>, "ExecutionContext must be move assignable!");
 static_assert(std::is_move_constructible_v<ExecutionContext>, "ExecutionContext must be move constructible!");
 
-inline bool MustAbort(const ExecutionContext &context) {
+inline bool MustAbort(const ExecutionContext &context) noexcept {
   return (context.is_shutting_down != nullptr && context.is_shutting_down->load(std::memory_order_acquire)) ||
          context.timer.IsExpired();
 }
diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp
index 26b7b6a19..078abd4ec 100644
--- a/src/query/db_accessor.hpp
+++ b/src/query/db_accessor.hpp
@@ -73,11 +73,11 @@ class EdgeAccessor final {
 
   int64_t CypherId() const { return impl_.Gid().AsInt(); }
 
-  auto Gid() const { return impl_.Gid(); }
+  storage::Gid Gid() const noexcept { return impl_.Gid(); }
 
-  bool operator==(const EdgeAccessor &e) const { return impl_ == e.impl_; }
+  bool operator==(const EdgeAccessor &e) const noexcept { return impl_ == e.impl_; }
 
-  bool operator!=(const EdgeAccessor &e) const { return !(*this == e); }
+  bool operator!=(const EdgeAccessor &e) const noexcept { return !(*this == e); }
 };
 
 class VertexAccessor final {
@@ -87,7 +87,7 @@ class VertexAccessor final {
   static EdgeAccessor MakeEdgeAccessor(const storage::EdgeAccessor impl) { return EdgeAccessor(impl); }
 
  public:
-  explicit VertexAccessor(storage::VertexAccessor impl) : impl_(std::move(impl)) {}
+  explicit VertexAccessor(storage::VertexAccessor impl) : impl_(impl) {}
 
   bool IsVisible(storage::View view) const { return impl_.IsVisible(view); }
 
@@ -158,11 +158,14 @@ class VertexAccessor final {
 
   int64_t CypherId() const { return impl_.Gid().AsInt(); }
 
-  auto Gid() const { return impl_.Gid(); }
+  storage::Gid Gid() const noexcept { return impl_.Gid(); }
 
-  bool operator==(const VertexAccessor &v) const { return impl_ == v.impl_; }
+  bool operator==(const VertexAccessor &v) const noexcept {
+    static_assert(noexcept(impl_ == v.impl_));
+    return impl_ == v.impl_;
+  }
 
-  bool operator!=(const VertexAccessor &v) const { return !(*this == v); }
+  bool operator!=(const VertexAccessor &v) const noexcept { return !(*this == v); }
 };
 
 inline VertexAccessor EdgeAccessor::To() const { return VertexAccessor(impl_.ToVertex()); }
diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp
index 8cf25ec77..b2c13c27b 100644
--- a/src/query/plan/operator.cpp
+++ b/src/query/plan/operator.cpp
@@ -3601,7 +3601,7 @@ std::unordered_map<std::string, int64_t> CallProcedure::GetAndResetCounters() {
 namespace {
 
 void CallCustomProcedure(const std::string_view &fully_qualified_procedure_name, const mgp_proc &proc,
-                         const std::vector<Expression *> &args, const mgp_graph &graph, ExpressionEvaluator *evaluator,
+                         const std::vector<Expression *> &args, mgp_graph &graph, ExpressionEvaluator *evaluator,
                          utils::MemoryResource *memory, std::optional<size_t> memory_limit, mgp_result *result) {
   static_assert(std::uses_allocator_v<mgp_value, utils::Allocator<mgp_value>>,
                 "Expected mgp_value to use custom allocator and makes STL "
diff --git a/src/query/procedure/cypher_types.hpp b/src/query/procedure/cypher_types.hpp
index e69122205..33ef5cc56 100644
--- a/src/query/procedure/cypher_types.hpp
+++ b/src/query/procedure/cypher_types.hpp
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string_view>
 
+#include "query/procedure/mg_procedure_helpers.hpp"
 #include "query/typed_value.hpp"
 #include "utils/memory.hpp"
 #include "utils/pmr/string.hpp"
@@ -50,7 +51,7 @@ class AnyType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "ANY"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return !mgp_value_is_null(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return !CallBool(mgp_value_is_null, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return !value.IsNull(); }
 };
@@ -59,7 +60,7 @@ class BoolType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "BOOLEAN"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_bool(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_bool, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsBool(); }
 };
@@ -68,7 +69,7 @@ class StringType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "STRING"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_string(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_string, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsString(); }
 };
@@ -77,7 +78,7 @@ class IntType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "INTEGER"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_int(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_int, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsInt(); }
 };
@@ -86,7 +87,7 @@ class FloatType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "FLOAT"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_double(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_double, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsDouble(); }
 };
@@ -96,7 +97,7 @@ class NumberType : public CypherType {
   std::string_view GetPresentableName() const override { return "NUMBER"; }
 
   bool SatisfiesType(const mgp_value &value) const override {
-    return mgp_value_is_int(&value) || mgp_value_is_double(&value);
+    return CallBool(mgp_value_is_int, &value) || CallBool(mgp_value_is_double, &value);
   }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsInt() || value.IsDouble(); }
@@ -106,7 +107,7 @@ class NodeType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "NODE"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_vertex(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_vertex, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsVertex(); }
 };
@@ -115,7 +116,7 @@ class RelationshipType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "RELATIONSHIP"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_edge(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_edge, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsEdge(); }
 };
@@ -124,7 +125,7 @@ class PathType : public CypherType {
  public:
   std::string_view GetPresentableName() const override { return "PATH"; }
 
-  bool SatisfiesType(const mgp_value &value) const override { return mgp_value_is_path(&value); }
+  bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_path, &value); }
 
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsPath(); }
 };
@@ -141,7 +142,8 @@ class MapType : public CypherType {
   std::string_view GetPresentableName() const override { return "MAP"; }
 
   bool SatisfiesType(const mgp_value &value) const override {
-    return mgp_value_is_map(&value) || mgp_value_is_vertex(&value) || mgp_value_is_edge(&value);
+    return CallBool(mgp_value_is_map, &value) || CallBool(mgp_value_is_vertex, &value) ||
+           CallBool(mgp_value_is_edge, &value);
   }
 
   bool SatisfiesType(const query::TypedValue &value) const override {
@@ -166,10 +168,15 @@ class ListType : public CypherType {
   std::string_view GetPresentableName() const override { return presentable_name_; }
 
   bool SatisfiesType(const mgp_value &value) const override {
-    if (!mgp_value_is_list(&value)) return false;
-    const auto *list = mgp_value_get_list(&value);
-    for (size_t i = 0; i < mgp_list_size(list); ++i) {
-      if (!element_type_->SatisfiesType(*mgp_list_at(list, i))) return false;
+    if (!CallBool(mgp_value_is_list, &value)) {
+      return false;
+    }
+    auto *list = Call<const mgp_list *>(mgp_value_get_list, &value);
+    const auto list_size = Call<size_t>(mgp_list_size, list);
+    for (size_t i = 0; i < list_size; ++i) {
+      if (!element_type_->SatisfiesType(*Call<const mgp_value *>(mgp_list_at, list, i))) {
+        return false;
+      };
     }
     return true;
   }
@@ -228,7 +235,7 @@ class NullableType : public CypherType {
   std::string_view GetPresentableName() const override { return presentable_name_; }
 
   bool SatisfiesType(const mgp_value &value) const override {
-    return mgp_value_is_null(&value) || type_->SatisfiesType(value);
+    return CallBool(mgp_value_is_null, &value) || type_->SatisfiesType(value);
   }
 
   bool SatisfiesType(const query::TypedValue &value) const override {
diff --git a/src/query/procedure/mg_procedure_helpers.hpp b/src/query/procedure/mg_procedure_helpers.hpp
new file mode 100644
index 000000000..792745f6a
--- /dev/null
+++ b/src/query/procedure/mg_procedure_helpers.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "mg_procedure.h"
+
+namespace query::procedure {
+template <typename TResult, typename TFunc, typename... TArgs>
+TResult Call(TFunc func, TArgs... args) {
+  static_assert(std::is_trivially_copyable_v<TFunc>);
+  static_assert((std::is_trivially_copyable_v<std::remove_reference_t<TArgs>> && ...));
+  TResult result{};
+  MG_ASSERT(func(args..., &result) == MGP_ERROR_NO_ERROR);
+  return result;
+}
+
+template <typename TFunc, typename... TArgs>
+bool CallBool(TFunc func, TArgs... args) {
+  return Call<int>(func, args...) != 0;
+}
+
+template <typename TObj>
+using MgpRawObjectDeleter = void (*)(TObj *);
+
+template <typename TObj>
+using MgpUniquePtr = std::unique_ptr<TObj, MgpRawObjectDeleter<TObj>>;
+
+template <typename TObj, typename TFunc, typename... TArgs>
+mgp_error CreateMgpObject(MgpUniquePtr<TObj> &obj, TFunc func, TArgs &&...args) {
+  TObj *raw_obj{nullptr};
+  const auto err = func(std::forward<TArgs>(args)..., &raw_obj);
+  obj.reset(raw_obj);
+  return err;
+}
+}  // namespace query::procedure
diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp
index e674c0a56..efd45be1c 100644
--- a/src/query/procedure/mg_procedure_impl.cpp
+++ b/src/query/procedure/mg_procedure_impl.cpp
@@ -3,10 +3,15 @@
 #include <algorithm>
 #include <cstddef>
 #include <cstring>
+#include <exception>
+#include <memory>
 #include <regex>
+#include <stdexcept>
 #include <type_traits>
+#include <utility>
 
 #include "module.hpp"
+#include "query/procedure/mg_procedure_helpers.hpp"
 #include "utils/algorithm.hpp"
 #include "utils/concepts.hpp"
 #include "utils/logging.hpp"
@@ -37,84 +42,170 @@ void *MgpAlignedAllocImpl(utils::MemoryResource &memory, const size_t size_in_by
   const size_t bytes_for_header = *maybe_bytes_for_header;
   const size_t alloc_size = bytes_for_header + size_in_bytes;
   if (alloc_size < size_in_bytes) return nullptr;
+
+  void *ptr = memory.Allocate(alloc_size, alloc_align);
+  char *data = reinterpret_cast<char *>(ptr) + bytes_for_header;
+  std::memcpy(data - sizeof(size_in_bytes), &size_in_bytes, sizeof(size_in_bytes));
+  std::memcpy(data - sizeof(size_in_bytes) - sizeof(alloc_align), &alloc_align, sizeof(alloc_align));
+  return data;
+}
+
+void MgpFreeImpl(utils::MemoryResource &memory, void *const p) noexcept {
   try {
-    void *ptr = memory.Allocate(alloc_size, alloc_align);
-    char *data = reinterpret_cast<char *>(ptr) + bytes_for_header;
-    std::memcpy(data - sizeof(size_in_bytes), &size_in_bytes, sizeof(size_in_bytes));
-    std::memcpy(data - sizeof(size_in_bytes) - sizeof(alloc_align), &alloc_align, sizeof(alloc_align));
-    return data;
+    if (!p) return;
+    char *const data = reinterpret_cast<char *>(p);
+    // Read the header containing size & alignment info.
+    size_t size_in_bytes{};
+    std::memcpy(&size_in_bytes, data - sizeof(size_in_bytes), sizeof(size_in_bytes));
+    size_t alloc_align{};
+    std::memcpy(&alloc_align, data - sizeof(size_in_bytes) - sizeof(alloc_align), sizeof(alloc_align));
+    // Reconstruct how many bytes we allocated on top of the original request.
+    // We need not check allocation request overflow, since we did so already in
+    // mgp_aligned_alloc.
+    const size_t header_size = sizeof(size_in_bytes) + sizeof(alloc_align);
+    const size_t bytes_for_header = *utils::RoundUint64ToMultiple(header_size, alloc_align);
+    const size_t alloc_size = bytes_for_header + size_in_bytes;
+    // Get the original ptr we allocated.
+    void *const original_ptr = data - bytes_for_header;
+    memory.Deallocate(original_ptr, alloc_size, alloc_align);
+  } catch (const utils::BasicException &be) {
+    spdlog::error("BasicException during the release of memory for query modules: {}", be.what());
+  } catch (const std::exception &e) {
+    spdlog::error("std::exception during the release of memory for query modules: {}", e.what());
   } catch (...) {
-    return nullptr;
+    spdlog::error("Unexpected throw during the release of memory for query modules");
   }
 }
+struct NonexistentObjectException : public utils::BasicException {
+  using utils::BasicException::BasicException;
+};
 
-void MgpFreeImpl(utils::MemoryResource &memory, void *const p) {
-  if (!p) return;
-  char *const data = reinterpret_cast<char *>(p);
-  // Read the header containing size & alignment info.
-  size_t size_in_bytes;
-  std::memcpy(&size_in_bytes, data - sizeof(size_in_bytes), sizeof(size_in_bytes));
-  size_t alloc_align;
-  std::memcpy(&alloc_align, data - sizeof(size_in_bytes) - sizeof(alloc_align), sizeof(alloc_align));
-  // Reconstruct how many bytes we allocated on top of the original request.
-  // We need not check allocation request overflow, since we did so already in
-  // mgp_aligned_alloc.
-  const size_t header_size = sizeof(size_in_bytes) + sizeof(alloc_align);
-  const size_t bytes_for_header = *utils::RoundUint64ToMultiple(header_size, alloc_align);
-  const size_t alloc_size = bytes_for_header + size_in_bytes;
-  // Get the original ptr we allocated.
-  void *const original_ptr = data - bytes_for_header;
-  memory.Deallocate(original_ptr, alloc_size, alloc_align);
+struct KeyAlreadyExistsException : public utils::BasicException {
+  using utils::BasicException::BasicException;
+};
+
+struct InsufficientBufferException : public utils::BasicException {
+  using utils::BasicException::BasicException;
+};
+
+template <typename TFunc, typename TReturn>
+concept ReturnsType = std::same_as<std::invoke_result_t<TFunc>, TReturn>;
+
+template <typename TFunc>
+concept ReturnsVoid = ReturnsType<TFunc, void>;
+
+template <ReturnsVoid TFunc>
+void WrapExceptionsHelper(TFunc &&func) {
+  std::forward<TFunc>(func)();
 }
 
+template <typename TFunc, typename TReturn = std::invoke_result_t<TFunc>>
+void WrapExceptionsHelper(TFunc &&func, TReturn *result) {
+  *result = {};
+  *result = std::forward<TFunc>(func)();
+}
+
+template <typename TFunc, typename... Args>
+[[nodiscard]] mgp_error WrapExceptions(TFunc &&func, Args &&...args) noexcept {
+  static_assert(sizeof...(args) <= 1, "WrapExceptions should have only one or zero parameter!");
+  try {
+    WrapExceptionsHelper(std::forward<TFunc>(func), std::forward<Args>(args)...);
+  } catch (const NonexistentObjectException &neoe) {
+    spdlog::error("Nonexistent object error during mg API call: {}", neoe.what());
+    return MGP_ERROR_NON_EXISTENT_OBJECT;
+  } catch (const KeyAlreadyExistsException &kaee) {
+    spdlog::error("Key already exists error during mg API call: {}", kaee.what());
+    return MGP_ERROR_KEY_ALREADY_EXISTS;
+  } catch (const InsufficientBufferException &ibe) {
+    spdlog::error("Insufficient buffer error during mg API call: {}", ibe.what());
+    return MGP_ERROR_INSUFFICIENT_BUFFER;
+  } catch (const std::bad_alloc &bae) {
+    spdlog::error("Memory allocation error during mg API call: {}", bae.what());
+    return MGP_ERROR_UNABLE_TO_ALLOCATE;
+  } catch (const utils::OutOfMemoryException &oome) {
+    spdlog::error("Memory limit exceeded during mg API call: {}", oome.what());
+    return MGP_ERROR_UNABLE_TO_ALLOCATE;
+  } catch (const std::out_of_range &oore) {
+    spdlog::error("Out of range error during mg API call: {}", oore.what());
+    return MGP_ERROR_OUT_OF_RANGE;
+  } catch (const std::invalid_argument &iae) {
+    spdlog::error("Invalid argument error during mg API call: {}", iae.what());
+    return MGP_ERROR_INVALID_ARGUMENT;
+  } catch (const std::logic_error &lee) {
+    spdlog::error("Logic error during mg API call: {}", lee.what());
+    return MGP_ERROR_LOGIC_ERROR;
+  } catch (const std::exception &e) {
+    spdlog::error("Unexpected error during mg API call: {}", e.what());
+    return MGP_ERROR_UNKNOWN_ERROR;
+  } catch (...) {
+    spdlog::error("Unexpected error during mg API call");
+    return MGP_ERROR_UNKNOWN_ERROR;
+  }
+  return MGP_ERROR_NO_ERROR;
+}
 }  // namespace
 
-void *mgp_alloc(mgp_memory *memory, size_t size_in_bytes) {
-  return mgp_aligned_alloc(memory, size_in_bytes, alignof(std::max_align_t));
+mgp_error mgp_alloc(mgp_memory *memory, size_t size_in_bytes, void **result) {
+  return mgp_aligned_alloc(memory, size_in_bytes, alignof(std::max_align_t), result);
 }
 
-void *mgp_aligned_alloc(mgp_memory *memory, const size_t size_in_bytes, const size_t alignment) {
-  return MgpAlignedAllocImpl(*memory->impl, size_in_bytes, alignment);
+mgp_error mgp_aligned_alloc(mgp_memory *memory, const size_t size_in_bytes, const size_t alignment, void **result) {
+  return WrapExceptions(
+      [memory, size_in_bytes, alignment] { return MgpAlignedAllocImpl(*memory->impl, size_in_bytes, alignment); },
+      result);
 }
 
-void mgp_free(mgp_memory *memory, void *const p) { MgpFreeImpl(*memory->impl, p); }
-
-void *mgp_global_alloc(size_t size_in_bytes) {
-  return mgp_global_aligned_alloc(size_in_bytes, alignof(std::max_align_t));
+void mgp_free(mgp_memory *memory, void *const p) {
+  static_assert(noexcept(MgpFreeImpl(*memory->impl, p)));
+  MgpFreeImpl(*memory->impl, p);
 }
 
-void *mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment) {
-  return MgpAlignedAllocImpl(gModuleRegistry.GetSharedMemoryResource(), size_in_bytes, alignment);
+mgp_error mgp_global_alloc(size_t size_in_bytes, void **result) {
+  return mgp_global_aligned_alloc(size_in_bytes, alignof(std::max_align_t), result);
 }
 
-void mgp_global_free(void *const p) { MgpFreeImpl(gModuleRegistry.GetSharedMemoryResource(), p); }
+mgp_error mgp_global_aligned_alloc(size_t size_in_bytes, size_t alignment, void **result) {
+  return WrapExceptions(
+      [size_in_bytes, alignment] {
+        return MgpAlignedAllocImpl(gModuleRegistry.GetSharedMemoryResource(), size_in_bytes, alignment);
+      },
+      result);
+}
+
+void mgp_global_free(void *const p) {
+  static_assert(noexcept(MgpFreeImpl(gModuleRegistry.GetSharedMemoryResource(), p)));
+  MgpFreeImpl(gModuleRegistry.GetSharedMemoryResource(), p);
+}
 
 namespace {
 
-// May throw whatever the constructor of U throws. `std::bad_alloc` is handled
-// by returning nullptr.
 template <class U, class... TArgs>
-U *new_mgp_object(utils::MemoryResource *memory, TArgs &&...args) {
+U *NewRawMgpObject(utils::MemoryResource *memory, TArgs &&...args) {
   utils::Allocator<U> allocator(memory);
-  try {
-    return allocator.template new_object<U>(std::forward<TArgs>(args)...);
-  } catch (const std::bad_alloc &) {
-    return nullptr;
-  }
+  return allocator.template new_object<U>(std::forward<TArgs>(args)...);
 }
 
 template <class U, class... TArgs>
-U *new_mgp_object(mgp_memory *memory, TArgs &&...args) {
-  return new_mgp_object<U, TArgs...>(memory->impl, std::forward<TArgs>(args)...);
+U *NewRawMgpObject(mgp_memory *memory, TArgs &&...args) {
+  return NewRawMgpObject<U, TArgs...>(memory->impl, std::forward<TArgs>(args)...);
 }
 
 // Assume that deallocation and object destruction never throws. If it does,
 // we are in big trouble.
 template <class T>
-void delete_mgp_object(T *ptr) noexcept {
-  if (!ptr) return;
-  utils::Allocator<T> allocator(ptr->GetMemoryResource());
-  allocator.delete_object(ptr);
+void DeleteRawMgpObject(T *ptr) noexcept {
+  try {
+    if (!ptr) return;
+    utils::Allocator<T> allocator(ptr->GetMemoryResource());
+    allocator.delete_object(ptr);
+  } catch (...) {
+    LOG_FATAL("Cannot deallocate mgp object");
+  }
+}
+
+template <class U, class... TArgs>
+MgpUniquePtr<U> NewMgpObject(mgp_memory *memory, TArgs &&...args) {
+  return MgpUniquePtr<U>(NewRawMgpObject<U>(memory->impl, std::forward<TArgs>(args)...), &DeleteRawMgpObject<U>);
 }
 
 mgp_value_type FromTypedValueType(query::TypedValue::Type type) {
@@ -143,19 +234,19 @@ mgp_value_type FromTypedValueType(query::TypedValue::Type type) {
 }
 
 query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memory) {
-  switch (mgp_value_get_type(&val)) {
+  switch (Call<mgp_value_type>(mgp_value_get_type, &val)) {
     case MGP_VALUE_TYPE_NULL:
       return query::TypedValue(memory);
     case MGP_VALUE_TYPE_BOOL:
-      return query::TypedValue(static_cast<bool>(mgp_value_get_bool(&val)), memory);
+      return query::TypedValue(CallBool(mgp_value_get_bool, &val), memory);
     case MGP_VALUE_TYPE_INT:
-      return query::TypedValue(mgp_value_get_int(&val), memory);
+      return query::TypedValue(Call<int64_t>(mgp_value_get_int, &val), memory);
     case MGP_VALUE_TYPE_DOUBLE:
-      return query::TypedValue(mgp_value_get_double(&val), memory);
+      return query::TypedValue(Call<double>(mgp_value_get_double, &val), memory);
     case MGP_VALUE_TYPE_STRING:
-      return query::TypedValue(mgp_value_get_string(&val), memory);
+      return query::TypedValue(Call<const char *>(mgp_value_get_string, &val), memory);
     case MGP_VALUE_TYPE_LIST: {
-      const auto *list = mgp_value_get_list(&val);
+      const auto *list = Call<const mgp_list *>(mgp_value_get_list, &val);
       query::TypedValue::TVector tv_list(memory);
       tv_list.reserve(list->elems.size());
       for (const auto &elem : list->elems) {
@@ -164,7 +255,7 @@ query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memo
       return query::TypedValue(std::move(tv_list));
     }
     case MGP_VALUE_TYPE_MAP: {
-      const auto *map = mgp_value_get_map(&val);
+      const auto *map = Call<const mgp_map *>(mgp_value_get_map, &val);
       query::TypedValue::TMap tv_map(memory);
       for (const auto &item : map->items) {
         tv_map.emplace(item.first, ToTypedValue(item.second, memory));
@@ -172,11 +263,11 @@ query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memo
       return query::TypedValue(std::move(tv_map));
     }
     case MGP_VALUE_TYPE_VERTEX:
-      return query::TypedValue(mgp_value_get_vertex(&val)->impl, memory);
+      return query::TypedValue(Call<const mgp_vertex *>(mgp_value_get_vertex, &val)->impl, memory);
     case MGP_VALUE_TYPE_EDGE:
-      return query::TypedValue(mgp_value_get_edge(&val)->impl, memory);
+      return query::TypedValue(Call<const mgp_edge *>(mgp_value_get_edge, &val)->impl, memory);
     case MGP_VALUE_TYPE_PATH: {
-      const auto *path = mgp_value_get_path(&val);
+      const auto *path = Call<const mgp_path *>(mgp_value_get_path, &val);
       MG_ASSERT(!path->vertices.empty());
       MG_ASSERT(path->vertices.size() == path->edges.size() + 1);
       query::Path tv_path(path->vertices[0].impl, memory);
@@ -406,7 +497,7 @@ namespace {
 void DeleteValueMember(mgp_value *value) noexcept {
   MG_ASSERT(value);
   utils::Allocator<mgp_value> allocator(value->GetMemoryResource());
-  switch (mgp_value_get_type(value)) {
+  switch (Call<mgp_value_type>(mgp_value_get_type, value)) {
     case MGP_VALUE_TYPE_NULL:
     case MGP_VALUE_TYPE_BOOL:
     case MGP_VALUE_TYPE_INT:
@@ -509,293 +600,393 @@ mgp_value::mgp_value(mgp_value &&other, utils::MemoryResource *m) : type(other.t
 
 mgp_value::~mgp_value() noexcept { DeleteValueMember(this); }
 
-void mgp_value_destroy(mgp_value *val) { delete_mgp_object(val); }
+void mgp_value_destroy(mgp_value *val) { DeleteRawMgpObject(val); }
 
-mgp_value *mgp_value_make_null(mgp_memory *memory) { return new_mgp_object<mgp_value>(memory); }
+mgp_error mgp_value_make_null(mgp_memory *memory, mgp_value **result) {
+  return WrapExceptions([memory] { return NewRawMgpObject<mgp_value>(memory); }, result);
+}
 
-mgp_value *mgp_value_make_bool(int val, mgp_memory *memory) { return new_mgp_object<mgp_value>(memory, val != 0); }
+mgp_error mgp_value_make_bool(int val, mgp_memory *memory, mgp_value **result) {
+  return WrapExceptions([val, memory] { return NewRawMgpObject<mgp_value>(memory, val != 0); }, result);
+}
 
-mgp_value *mgp_value_make_int(int64_t val, mgp_memory *memory) { return new_mgp_object<mgp_value>(memory, val); }
-
-mgp_value *mgp_value_make_double(double val, mgp_memory *memory) { return new_mgp_object<mgp_value>(memory, val); }
-
-mgp_value *mgp_value_make_string(const char *val, mgp_memory *memory) {
-  try {
-    // This may throw something from std::string constructor, it could be
-    // std::length_error, but it's not really well defined, so catch all.
-    return new_mgp_object<mgp_value>(memory, val);
-  } catch (...) {
-    return nullptr;
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define DEFINE_MGP_VALUE_MAKE_WITH_MEMORY(type, param)                                                \
+  mgp_error mgp_value_make_##type(param val, mgp_memory *memory, mgp_value **result) {                \
+    return WrapExceptions([val, memory] { return NewRawMgpObject<mgp_value>(memory, val); }, result); \
   }
-}
 
-mgp_value *mgp_value_make_list(mgp_list *val) { return new_mgp_object<mgp_value>(val->GetMemoryResource(), val); }
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+DEFINE_MGP_VALUE_MAKE_WITH_MEMORY(int, int64_t);
+DEFINE_MGP_VALUE_MAKE_WITH_MEMORY(double, double);
+DEFINE_MGP_VALUE_MAKE_WITH_MEMORY(string, const char *);
 
-mgp_value *mgp_value_make_map(mgp_map *val) { return new_mgp_object<mgp_value>(val->GetMemoryResource(), val); }
-
-mgp_value *mgp_value_make_vertex(mgp_vertex *val) { return new_mgp_object<mgp_value>(val->GetMemoryResource(), val); }
-
-mgp_value *mgp_value_make_edge(mgp_edge *val) { return new_mgp_object<mgp_value>(val->GetMemoryResource(), val); }
-
-mgp_value *mgp_value_make_path(mgp_path *val) { return new_mgp_object<mgp_value>(val->GetMemoryResource(), val); }
-
-mgp_value_type mgp_value_get_type(const mgp_value *val) { return val->type; }
-
-int mgp_value_is_null(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_NULL; }
-
-int mgp_value_is_bool(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_BOOL; }
-
-int mgp_value_is_int(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_INT; }
-
-int mgp_value_is_double(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_DOUBLE; }
-
-int mgp_value_is_string(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_STRING; }
-
-int mgp_value_is_list(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_LIST; }
-
-int mgp_value_is_map(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_MAP; }
-
-int mgp_value_is_vertex(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_VERTEX; }
-
-int mgp_value_is_edge(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_EDGE; }
-
-int mgp_value_is_path(const mgp_value *val) { return mgp_value_get_type(val) == MGP_VALUE_TYPE_PATH; }
-
-int mgp_value_get_bool(const mgp_value *val) { return val->bool_v ? 1 : 0; }
-
-int64_t mgp_value_get_int(const mgp_value *val) { return val->int_v; }
-
-double mgp_value_get_double(const mgp_value *val) { return val->double_v; }
-
-const char *mgp_value_get_string(const mgp_value *val) { return val->string_v.c_str(); }
-
-const mgp_list *mgp_value_get_list(const mgp_value *val) { return val->list_v; }
-
-const mgp_map *mgp_value_get_map(const mgp_value *val) { return val->map_v; }
-
-const mgp_vertex *mgp_value_get_vertex(const mgp_value *val) { return val->vertex_v; }
-
-const mgp_edge *mgp_value_get_edge(const mgp_value *val) { return val->edge_v; }
-
-const mgp_path *mgp_value_get_path(const mgp_value *val) { return val->path_v; }
-
-mgp_list *mgp_list_make_empty(size_t capacity, mgp_memory *memory) {
-  auto *list = new_mgp_object<mgp_list>(memory);
-  if (!list) return nullptr;
-  try {
-    // May throw std::bad_alloc or std::length_error.
-    list->elems.reserve(capacity);
-  } catch (...) {
-    mgp_list_destroy(list);
-    return nullptr;
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define DEFINE_MGP_VALUE_MAKE(type)                                                                             \
+  mgp_error mgp_value_make_##type(mgp_##type *val, mgp_value **result) {                                        \
+    return WrapExceptions([val] { return NewRawMgpObject<mgp_value>(val->GetMemoryResource(), val); }, result); \
   }
-  return list;
+
+DEFINE_MGP_VALUE_MAKE(list)
+DEFINE_MGP_VALUE_MAKE(map)
+DEFINE_MGP_VALUE_MAKE(vertex)
+DEFINE_MGP_VALUE_MAKE(edge)
+DEFINE_MGP_VALUE_MAKE(path)
+
+namespace {
+mgp_value_type MgpValueGetType(const mgp_value &val) noexcept { return val.type; }
+}  // namespace
+
+mgp_error mgp_value_get_type(const mgp_value *val, mgp_value_type *result) {
+  static_assert(noexcept(MgpValueGetType(*val)));
+  *result = MgpValueGetType(*val);
+  return MGP_ERROR_NO_ERROR;
 }
 
-void mgp_list_destroy(mgp_list *list) { delete_mgp_object(list); }
-
-int mgp_list_append(mgp_list *list, const mgp_value *val) {
-  if (mgp_list_size(list) >= mgp_list_capacity(list)) return 0;
-  return mgp_list_append_extend(list, val);
-}
-
-int mgp_list_append_extend(mgp_list *list, const mgp_value *val) {
-  try {
-    // May throw std::bad_alloc or std::length_error.
-    list->elems.push_back(*val);
-  } catch (...) {
-    return 0;
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define DEFINE_MGP_VALUE_IS(type_lowercase, type_uppercase)                    \
+  mgp_error mgp_value_is_##type_lowercase(const mgp_value *val, int *result) { \
+    static_assert(noexcept(MgpValueGetType(*val)));                            \
+    *result = MgpValueGetType(*val) == MGP_VALUE_TYPE_##type_uppercase;        \
+    return MGP_ERROR_NO_ERROR;                                                 \
   }
-  return 1;
+
+DEFINE_MGP_VALUE_IS(null, NULL)
+DEFINE_MGP_VALUE_IS(bool, BOOL)
+DEFINE_MGP_VALUE_IS(int, INT)
+DEFINE_MGP_VALUE_IS(double, DOUBLE)
+DEFINE_MGP_VALUE_IS(string, STRING)
+DEFINE_MGP_VALUE_IS(list, LIST)
+DEFINE_MGP_VALUE_IS(map, MAP)
+DEFINE_MGP_VALUE_IS(vertex, VERTEX)
+DEFINE_MGP_VALUE_IS(edge, EDGE)
+DEFINE_MGP_VALUE_IS(path, PATH)
+
+mgp_error mgp_value_get_bool(const mgp_value *val, int *result) {
+  *result = val->bool_v ? 1 : 0;
+  return MGP_ERROR_NO_ERROR;
+}
+mgp_error mgp_value_get_int(const mgp_value *val, int64_t *result) {
+  *result = val->int_v;
+  return MGP_ERROR_NO_ERROR;
+}
+mgp_error mgp_value_get_double(const mgp_value *val, double *result) {
+  *result = val->double_v;
+  return MGP_ERROR_NO_ERROR;
+}
+mgp_error mgp_value_get_string(const mgp_value *val, const char **result) {
+  static_assert(noexcept(val->string_v.c_str()));
+  *result = val->string_v.c_str();
+  return MGP_ERROR_NO_ERROR;
 }
 
-size_t mgp_list_size(const mgp_list *list) { return list->elems.size(); }
-
-size_t mgp_list_capacity(const mgp_list *list) { return list->elems.capacity(); }
-
-const mgp_value *mgp_list_at(const mgp_list *list, size_t i) {
-  if (i >= mgp_list_size(list)) return nullptr;
-  return &list->elems[i];
-}
-
-mgp_map *mgp_map_make_empty(mgp_memory *memory) { return new_mgp_object<mgp_map>(memory); }
-
-void mgp_map_destroy(mgp_map *map) { delete_mgp_object(map); }
-
-int mgp_map_insert(mgp_map *map, const char *key, const mgp_value *value) {
-  try {
-    // Unfortunately, cppreference.com does not say what exceptions are thrown,
-    // so catch all of them. It's probably `std::bad_alloc` and
-    // `std::length_error`.
-    map->items.emplace(key, *value);
-    return 1;
-  } catch (...) {
-    return 0;
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define DEFINE_MGP_VALUE_GET(type)                                                  \
+  mgp_error mgp_value_get_##type(const mgp_value *val, const mgp_##type **result) { \
+    *result = val->type##_v;                                                        \
+    return MGP_ERROR_NO_ERROR;                                                      \
   }
+
+DEFINE_MGP_VALUE_GET(list)
+DEFINE_MGP_VALUE_GET(map)
+DEFINE_MGP_VALUE_GET(vertex)
+DEFINE_MGP_VALUE_GET(edge)
+DEFINE_MGP_VALUE_GET(path)
+
+mgp_error mgp_list_make_empty(size_t capacity, mgp_memory *memory, mgp_list **result) {
+  return WrapExceptions(
+      [capacity, memory] {
+        auto list = NewMgpObject<mgp_list>(memory);
+        list->elems.reserve(capacity);
+        return list.release();
+      },
+      result);
 }
 
-size_t mgp_map_size(const mgp_map *map) { return map->items.size(); }
+void mgp_list_destroy(mgp_list *list) { DeleteRawMgpObject(list); }
 
-const mgp_value *mgp_map_at(const mgp_map *map, const char *key) {
-  auto found_it = map->items.find(key);
-  if (found_it == map->items.end()) return nullptr;
-  return &found_it->second;
+namespace {
+void MgpListAppendExtend(mgp_list &list, const mgp_value &value) { list.elems.push_back(value); }
+}  // namespace
+
+mgp_error mgp_list_append(mgp_list *list, const mgp_value *val) {
+  return WrapExceptions([list, val] {
+    if (Call<size_t>(mgp_list_size, list) >= Call<size_t>(mgp_list_capacity, list)) {
+      throw InsufficientBufferException{
+          "Cannot append a new value to the mgp_list without extending it, because its size reached its capacity!"};
+    }
+    MgpListAppendExtend(*list, *val);
+  });
 }
 
-const char *mgp_map_item_key(const mgp_map_item *item) { return item->key; }
-
-const mgp_value *mgp_map_item_value(const mgp_map_item *item) { return item->value; }
-
-mgp_map_items_iterator *mgp_map_iter_items(const mgp_map *map, mgp_memory *memory) {
-  return new_mgp_object<mgp_map_items_iterator>(memory, map);
+mgp_error mgp_list_append_extend(mgp_list *list, const mgp_value *val) {
+  return WrapExceptions([list, val] { MgpListAppendExtend(*list, *val); });
 }
 
-void mgp_map_items_iterator_destroy(mgp_map_items_iterator *it) { delete_mgp_object(it); }
-
-const mgp_map_item *mgp_map_items_iterator_get(const mgp_map_items_iterator *it) {
-  if (it->current_it == it->map->items.end()) return nullptr;
-  return &it->current;
+mgp_error mgp_list_size(const mgp_list *list, size_t *result) {
+  static_assert(noexcept(list->elems.size()));
+  *result = list->elems.size();
+  return MGP_ERROR_NO_ERROR;
 }
 
-const mgp_map_item *mgp_map_items_iterator_next(mgp_map_items_iterator *it) {
-  if (it->current_it == it->map->items.end()) return nullptr;
-  if (++it->current_it == it->map->items.end()) return nullptr;
-  it->current.key = it->current_it->first.c_str();
-  it->current.value = &it->current_it->second;
-  return &it->current;
+mgp_error mgp_list_capacity(const mgp_list *list, size_t *result) {
+  static_assert(noexcept(list->elems.capacity()));
+  *result = list->elems.capacity();
+  return MGP_ERROR_NO_ERROR;
 }
 
-mgp_path *mgp_path_make_with_start(const mgp_vertex *vertex, mgp_memory *memory) {
-  auto *path = new_mgp_object<mgp_path>(memory);
-  if (!path) return nullptr;
-  try {
-    path->vertices.push_back(*vertex);
-  } catch (...) {
-    delete_mgp_object(path);
-    return nullptr;
-  }
-  return path;
+mgp_error mgp_list_at(const mgp_list *list, size_t i, const mgp_value **result) {
+  return WrapExceptions(
+      [list, i] {
+        if (i >= Call<size_t>(mgp_list_size, list)) {
+          throw std::out_of_range("Element cannot be retrieved, because index exceeds list's size!");
+        }
+        return &list->elems[i];
+      },
+      result);
 }
 
-mgp_path *mgp_path_copy(const mgp_path *path, mgp_memory *memory) {
-  MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1, "Invalid mgp_path");
-  return new_mgp_object<mgp_path>(memory, *path);
+mgp_error mgp_map_make_empty(mgp_memory *memory, mgp_map **result) {
+  return WrapExceptions([&memory] { return NewRawMgpObject<mgp_map>(memory); }, result);
 }
 
-void mgp_path_destroy(mgp_path *path) { delete_mgp_object(path); }
+void mgp_map_destroy(mgp_map *map) { DeleteRawMgpObject(map); }
+
+mgp_error mgp_map_insert(mgp_map *map, const char *key, const mgp_value *value) {
+  return WrapExceptions([&] {
+    auto emplace_result = map->items.emplace(key, *value);
+    if (!emplace_result.second) {
+      throw KeyAlreadyExistsException{"Map already contains mapping for {}", key};
+    }
+  });
+}
+
+mgp_error mgp_map_size(const mgp_map *map, size_t *result) {
+  static_assert(noexcept(map->items.size()));
+  *result = map->items.size();
+  return MGP_ERROR_NO_ERROR;
+}
+
+mgp_error mgp_map_at(const mgp_map *map, const char *key, const mgp_value **result) {
+  return WrapExceptions(
+      [&map, &key]() -> const mgp_value * {
+        auto found_it = map->items.find(key);
+        if (found_it == map->items.end()) {
+          return nullptr;
+        };
+        return &found_it->second;
+      },
+      result);
+}
+
+mgp_error mgp_map_item_key(mgp_map_item *item, const char **result) {
+  return WrapExceptions([&item] { return item->key; }, result);
+}
+
+mgp_error mgp_map_item_value(const mgp_map_item *item, const mgp_value **result) {
+  return WrapExceptions([item] { return item->value; }, result);
+}
+
+mgp_error mgp_map_iter_items(mgp_map *map, mgp_memory *memory, mgp_map_items_iterator **result) {
+  return WrapExceptions([map, memory] { return NewRawMgpObject<mgp_map_items_iterator>(memory, map); }, result);
+}
+
+void mgp_map_items_iterator_destroy(mgp_map_items_iterator *it) { DeleteRawMgpObject(it); }
+
+mgp_error mgp_map_items_iterator_get(const mgp_map_items_iterator *it, const mgp_map_item **result) {
+  return WrapExceptions(
+      [it]() -> const mgp_map_item * {
+        if (it->current_it == it->map->items.end()) {
+          return nullptr;
+        };
+        return &it->current;
+      },
+      result);
+}
+
+mgp_error mgp_map_items_iterator_next(mgp_map_items_iterator *it, const mgp_map_item **result) {
+  return WrapExceptions(
+      [it]() -> const mgp_map_item * {
+        if (it->current_it == it->map->items.end()) {
+          return nullptr;
+        }
+        if (++it->current_it == it->map->items.end()) {
+          return nullptr;
+        }
+        it->current.key = it->current_it->first.c_str();
+        it->current.value = &it->current_it->second;
+        return &it->current;
+      },
+      result);
+}
+
+mgp_error mgp_path_make_with_start(const mgp_vertex *vertex, mgp_memory *memory, mgp_path **result) {
+  return WrapExceptions(
+      [vertex, memory]() -> mgp_path * {
+        auto path = NewMgpObject<mgp_path>(memory);
+        if (path == nullptr) {
+          return nullptr;
+        }
+        path->vertices.push_back(*vertex);
+        return path.release();
+      },
+      result);
+}
+
+mgp_error mgp_path_copy(const mgp_path *path, mgp_memory *memory, mgp_path **result) {
+  return WrapExceptions(
+      [path, memory] {
+        MG_ASSERT(Call<size_t>(mgp_path_size, path) == path->vertices.size() - 1, "Invalid mgp_path");
+        return NewRawMgpObject<mgp_path>(memory, *path);
+      },
+      result);
+}
+
+void mgp_path_destroy(mgp_path *path) { DeleteRawMgpObject(path); }
+
+mgp_error mgp_path_expand(mgp_path *path, const mgp_edge *edge) {
+  return WrapExceptions([path, edge] {
+    MG_ASSERT(Call<size_t>(mgp_path_size, path) == path->vertices.size() - 1, "Invalid mgp_path");
+    // Check that the both the last vertex on path and dst_vertex are endpoints of
+    // the given edge.
+    const auto *src_vertex = &path->vertices.back();
+    const mgp_vertex *dst_vertex = nullptr;
+    if (CallBool(mgp_vertex_equal, Call<const mgp_vertex *>(mgp_edge_get_to, edge), src_vertex) != 0) {
+      dst_vertex = Call<const mgp_vertex *>(mgp_edge_get_from, edge);
+    } else if (CallBool(mgp_vertex_equal, Call<const mgp_vertex *>(mgp_edge_get_from, edge), src_vertex)) {
+      dst_vertex = Call<const mgp_vertex *>(mgp_edge_get_to, edge);
+    } else {
+      // edge is not a continuation on src_vertex
+      throw std::logic_error{"The current last vertex in the path is not part of the given edge."};
+    }
+    // Try appending edge and dst_vertex to path, preserving the original mgp_path
+    // instance if anything fails.
+    utils::OnScopeExit scope_guard(
+        [path] { MG_ASSERT(Call<size_t>(mgp_path_size, path) == path->vertices.size() - 1); });
 
-int mgp_path_expand(mgp_path *path, const mgp_edge *edge) {
-  MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1, "Invalid mgp_path");
-  // Check that the both the last vertex on path and dst_vertex are endpoints of
-  // the given edge.
-  const auto *src_vertex = &path->vertices.back();
-  const mgp_vertex *dst_vertex = nullptr;
-  if (mgp_vertex_equal(mgp_edge_get_to(edge), src_vertex)) {
-    dst_vertex = mgp_edge_get_from(edge);
-  } else if (mgp_vertex_equal(mgp_edge_get_from(edge), src_vertex)) {
-    dst_vertex = mgp_edge_get_to(edge);
-  } else {
-    // edge is not a continuation on src_vertex
-    return 0;
-  }
-  // Try appending edge and dst_vertex to path, preserving the original mgp_path
-  // instance if anything fails.
-  try {
     path->edges.push_back(*edge);
-  } catch (...) {
-    MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1);
-    return 0;
-  }
-  try {
     path->vertices.push_back(*dst_vertex);
-  } catch (...) {
-    path->edges.pop_back();
-    MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1);
-    return 0;
-  }
-  MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1);
-  return 1;
+  });
 }
 
-size_t mgp_path_size(const mgp_path *path) { return path->edges.size(); }
+namespace {
+size_t MgpPathSize(const mgp_path &path) noexcept { return path.edges.size(); }
+}  // namespace
 
-const mgp_vertex *mgp_path_vertex_at(const mgp_path *path, size_t i) {
-  MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1);
-  if (i > mgp_path_size(path)) return nullptr;
-  return &path->vertices[i];
+mgp_error mgp_path_size(const mgp_path *path, size_t *result) {
+  *result = MgpPathSize(*path);
+  return MGP_ERROR_NO_ERROR;
 }
 
-const mgp_edge *mgp_path_edge_at(const mgp_path *path, size_t i) {
-  MG_ASSERT(mgp_path_size(path) == path->vertices.size() - 1);
-  if (i >= mgp_path_size(path)) return nullptr;
-  return &path->edges[i];
+mgp_error mgp_path_vertex_at(const mgp_path *path, size_t i, const mgp_vertex **result) {
+  return WrapExceptions(
+      [path, i] {
+        const auto path_size = Call<size_t>(mgp_path_size, path);
+        MG_ASSERT(path_size == path->vertices.size() - 1);
+        if (i > path_size) {
+          throw std::out_of_range("Vertex cannot be retrieved, because index exceeds path's size!");
+        }
+        return &path->vertices[i];
+      },
+      result);
 }
 
-int mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2) {
-  MG_ASSERT(mgp_path_size(p1) == p1->vertices.size() - 1);
-  MG_ASSERT(mgp_path_size(p2) == p2->vertices.size() - 1);
-  if (mgp_path_size(p1) != mgp_path_size(p2)) return 0;
-  const auto *start1 = mgp_path_vertex_at(p1, 0);
-  const auto *start2 = mgp_path_vertex_at(p2, 0);
-  if (!mgp_vertex_equal(start1, start2)) return 0;
-  for (size_t i = 0; i < mgp_path_size(p1); ++i) {
-    const auto *e1 = mgp_path_edge_at(p1, i);
-    const auto *e2 = mgp_path_edge_at(p2, i);
-    if (!mgp_edge_equal(e1, e2)) return 0;
-  }
-  return 1;
+mgp_error mgp_path_edge_at(const mgp_path *path, size_t i, const mgp_edge **result) {
+  return WrapExceptions(
+      [path, i] {
+        const auto path_size = Call<size_t>(mgp_path_size, path);
+        MG_ASSERT(path_size == path->vertices.size() - 1);
+        if (i > path_size) {
+          throw std::out_of_range("Edge cannot be retrieved, because index exceeds path's size!");
+        }
+        return &path->edges[i];
+      },
+      result);
+}
+
+mgp_error mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2, int *result) {
+  return WrapExceptions(
+      [p1, p2] {
+        const auto p1_size = MgpPathSize(*p1);
+        const auto p2_size = MgpPathSize(*p2);
+        MG_ASSERT(p1_size == p1->vertices.size() - 1);
+        MG_ASSERT(p2_size == p2->vertices.size() - 1);
+        if (p1_size != p2_size) {
+          return 0;
+        }
+        const auto *start1 = Call<const mgp_vertex *>(mgp_path_vertex_at, p1, 0);
+        const auto *start2 = Call<const mgp_vertex *>(mgp_path_vertex_at, p2, 0);
+        static_assert(noexcept(start1->impl == start2->impl));
+        if (*start1 != *start2) {
+          return 0;
+        }
+        for (size_t i = 0; i < p1_size; ++i) {
+          const auto *e1 = Call<const mgp_edge *>(mgp_path_edge_at, p1, i);
+          const auto *e2 = Call<const mgp_edge *>(mgp_path_edge_at, p2, i);
+          if (*e1 != *e2) {
+            return 0;
+          }
+        }
+        return 1;
+      },
+      result);
 }
 
 /// Plugin Result
 
-int mgp_result_set_error_msg(mgp_result *res, const char *msg) {
-  auto *memory = res->rows.get_allocator().GetMemoryResource();
-  try {
+mgp_error mgp_result_set_error_msg(mgp_result *res, const char *msg) {
+  return WrapExceptions([=] {
+    auto *memory = res->rows.get_allocator().GetMemoryResource();
     res->error_msg.emplace(msg, memory);
-  } catch (...) {
-    return 0;
-  }
-  return 1;
+  });
 }
 
-mgp_result_record *mgp_result_new_record(mgp_result *res) {
-  auto *memory = res->rows.get_allocator().GetMemoryResource();
-  MG_ASSERT(res->signature, "Expected to have a valid signature");
-  try {
-    res->rows.push_back(
-        mgp_result_record{res->signature, utils::pmr::map<utils::pmr::string, query::TypedValue>(memory)});
-  } catch (...) {
-    return nullptr;
-  }
-  return &res->rows.back();
+mgp_error mgp_result_new_record(mgp_result *res, mgp_result_record **result) {
+  return WrapExceptions(
+      [res] {
+        auto *memory = res->rows.get_allocator().GetMemoryResource();
+        MG_ASSERT(res->signature, "Expected to have a valid signature");
+        res->rows.push_back(
+            mgp_result_record{res->signature, utils::pmr::map<utils::pmr::string, query::TypedValue>(memory)});
+        return &res->rows.back();
+      },
+      result);
 }
 
-int mgp_result_record_insert(mgp_result_record *record, const char *field_name, const mgp_value *val) {
-  auto *memory = record->values.get_allocator().GetMemoryResource();
-  // Validate field_name & val satisfy the procedure's result signature.
-  MG_ASSERT(record->signature, "Expected to have a valid signature");
-  auto find_it = record->signature->find(field_name);
-  if (find_it == record->signature->end()) return 0;
-  const auto *type = find_it->second.first;
-  if (!type->SatisfiesType(*val)) return 0;
-  try {
+mgp_error mgp_result_record_insert(mgp_result_record *record, const char *field_name, const mgp_value *val) {
+  return WrapExceptions([=] {
+    auto *memory = record->values.get_allocator().GetMemoryResource();
+    // Validate field_name & val satisfy the procedure's result signature.
+    MG_ASSERT(record->signature, "Expected to have a valid signature");
+    auto find_it = record->signature->find(field_name);
+    if (find_it == record->signature->end()) {
+      throw std::out_of_range{fmt::format("The result doesn't have any field named '{}'.", field_name)};
+    }
+    const auto *type = find_it->second.first;
+    if (!type->SatisfiesType(*val)) {
+      throw std::logic_error{
+          fmt::format("The type of value doesn't satisfies the type '{}'!", type->GetPresentableName())};
+    }
     record->values.emplace(field_name, ToTypedValue(*val, memory));
-  } catch (...) {
-    return 0;
-  }
-  return 1;
+  });
 }
 
 /// Graph Constructs
 
-void mgp_properties_iterator_destroy(mgp_properties_iterator *it) { delete_mgp_object(it); }
+void mgp_properties_iterator_destroy(mgp_properties_iterator *it) { DeleteRawMgpObject(it); }
 
-const mgp_property *mgp_properties_iterator_get(const mgp_properties_iterator *it) {
-  if (it->current) return &it->property;
-  return nullptr;
+mgp_error mgp_properties_iterator_get(const mgp_properties_iterator *it, const mgp_property **result) {
+  return WrapExceptions(
+      [it]() -> const mgp_property * {
+        if (it->current) {
+          return &it->property;
+        };
+        return nullptr;
+      },
+      result);
 }
 
-const mgp_property *mgp_properties_iterator_next(mgp_properties_iterator *it) {
+mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, const mgp_property **result) {
   // Incrementing the iterator either for on-disk or in-memory
   // storage, so perhaps the underlying thing can throw.
   // Both copying TypedValue and/or string from PropertyName may fail to
@@ -803,382 +994,400 @@ const mgp_property *mgp_properties_iterator_next(mgp_properties_iterator *it) {
   // either way return nullptr and leave `it` in undefined state.
   // Hopefully iterator comparison doesn't throw, but wrap the whole thing in
   // try ... catch just to be sure.
-  try {
-    if (it->current_it == it->pvs.end()) {
-      MG_ASSERT(!it->current,
-                "Iteration is already done, so it->current should "
-                "have been set to std::nullopt");
-      return nullptr;
-    }
-    if (++it->current_it == it->pvs.end()) {
-      it->current = std::nullopt;
-      return nullptr;
-    }
-    it->current.emplace(
-        utils::pmr::string(it->graph->impl->PropertyToName(it->current_it->first), it->GetMemoryResource()),
-        mgp_value(it->current_it->second, it->GetMemoryResource()));
-    it->property.name = it->current->first.c_str();
-    it->property.value = &it->current->second;
-    return &it->property;
-  } catch (...) {
-    it->current = std::nullopt;
-    return nullptr;
-  }
-}
-
-mgp_vertex_id mgp_vertex_get_id(const mgp_vertex *v) { return mgp_vertex_id{.as_int = v->impl.Gid().AsInt()}; }
-
-mgp_vertex *mgp_vertex_copy(const mgp_vertex *v, mgp_memory *memory) { return new_mgp_object<mgp_vertex>(memory, *v); }
-
-void mgp_vertex_destroy(mgp_vertex *v) { delete_mgp_object(v); }
-
-int mgp_vertex_equal(const mgp_vertex *a, const mgp_vertex *b) { return a->impl == b->impl ? 1 : 0; }
-
-size_t mgp_vertex_labels_count(const mgp_vertex *v) {
-  auto maybe_labels = v->impl.Labels(v->graph->view);
-  if (maybe_labels.HasError()) {
-    switch (maybe_labels.GetError()) {
-      case storage::Error::DELETED_OBJECT:
-      case storage::Error::NONEXISTENT_OBJECT:
-        // Treat deleted/nonexistent vertex as having no labels.
-        return 0;
-      case storage::Error::PROPERTIES_DISABLED:
-      case storage::Error::VERTEX_HAS_EDGES:
-      case storage::Error::SERIALIZATION_ERROR:
-        spdlog::error("Unexpected error when getting vertex labels.");
-        return 0;
-    }
-  }
-  return maybe_labels->size();
-}
-
-mgp_label mgp_vertex_label_at(const mgp_vertex *v, size_t i) {
-  // TODO: Maybe it's worth caching this in mgp_vertex.
-  auto maybe_labels = v->impl.Labels(v->graph->view);
-  if (maybe_labels.HasError()) {
-    switch (maybe_labels.GetError()) {
-      case storage::Error::DELETED_OBJECT:
-      case storage::Error::NONEXISTENT_OBJECT:
-        return mgp_label{nullptr};
-      case storage::Error::PROPERTIES_DISABLED:
-      case storage::Error::VERTEX_HAS_EDGES:
-      case storage::Error::SERIALIZATION_ERROR:
-        spdlog::error("Unexpected error when getting vertex labels.");
-        return mgp_label{nullptr};
-    }
-  }
-  if (i >= maybe_labels->size()) return mgp_label{nullptr};
-  const auto &label = (*maybe_labels)[i];
-  static_assert(std::is_lvalue_reference_v<decltype(v->graph->impl->LabelToName(label))>,
-                "Expected LabelToName to return a pointer or reference, so we "
-                "don't have to take a copy and manage memory.");
-  const auto &name = v->graph->impl->LabelToName(label);
-  return mgp_label{name.c_str()};
-}
-
-int mgp_vertex_has_label_named(const mgp_vertex *v, const char *name) {
-  storage::LabelId label;
-  try {
-    // This will allocate a std::string from `name`, which may throw
-    // std::bad_alloc or std::length_error. This could be avoided with a
-    // std::string_view. Although storage API may be updated with
-    // std::string_view, NameToLabel itself may still throw std::bad_alloc when
-    // creating a new LabelId mapping and we need to handle that.
-    label = v->graph->impl->NameToLabel(name);
-  } catch (...) {
-    spdlog::error("Unable to allocate a LabelId mapping");
-    // If we need to allocate a new mapping, then the vertex does not have such
-    // a label, so return 0.
-    return 0;
-  }
-  auto maybe_has_label = v->impl.HasLabel(v->graph->view, label);
-  if (maybe_has_label.HasError()) {
-    switch (maybe_has_label.GetError()) {
-      case storage::Error::DELETED_OBJECT:
-      case storage::Error::NONEXISTENT_OBJECT:
-        return 0;
-      case storage::Error::PROPERTIES_DISABLED:
-      case storage::Error::VERTEX_HAS_EDGES:
-      case storage::Error::SERIALIZATION_ERROR:
-        spdlog::error("Unexpected error when checking vertex has label.");
-        return 0;
-    }
-  }
-  return *maybe_has_label;
-}
-
-int mgp_vertex_has_label(const mgp_vertex *v, mgp_label label) { return mgp_vertex_has_label_named(v, label.name); }
-
-mgp_value *mgp_vertex_get_property(const mgp_vertex *v, const char *name, mgp_memory *memory) {
-  try {
-    const auto &key = v->graph->impl->NameToProperty(name);
-    auto maybe_prop = v->impl.GetProperty(v->graph->view, key);
-    if (maybe_prop.HasError()) {
-      switch (maybe_prop.GetError()) {
-        case storage::Error::DELETED_OBJECT:
-        case storage::Error::NONEXISTENT_OBJECT:
-          // Treat deleted/nonexistent vertex as having no properties.
-          return new_mgp_object<mgp_value>(memory);
-        case storage::Error::PROPERTIES_DISABLED:
-        case storage::Error::VERTEX_HAS_EDGES:
-        case storage::Error::SERIALIZATION_ERROR:
-          spdlog::error("Unexpected error when getting vertex property");
+  return WrapExceptions(
+      [it]() -> const mgp_property * {
+        if (it->current_it == it->pvs.end()) {
+          MG_ASSERT(!it->current,
+                    "Iteration is already done, so it->current should "
+                    "have been set to std::nullopt");
           return nullptr;
-      }
-    }
-    return new_mgp_object<mgp_value>(memory, std::move(*maybe_prop));
-  } catch (...) {
-    // In case NameToProperty or GetProperty throw an exception, most likely
-    // std::bad_alloc.
-    return nullptr;
-  }
+        }
+        if (++it->current_it == it->pvs.end()) {
+          it->current = std::nullopt;
+          return nullptr;
+        }
+        utils::OnScopeExit clean_up([it] { it->current = std::nullopt; });
+        it->current.emplace(
+            utils::pmr::string(it->graph->impl->PropertyToName(it->current_it->first), it->GetMemoryResource()),
+            mgp_value(it->current_it->second, it->GetMemoryResource()));
+        it->property.name = it->current->first.c_str();
+        it->property.value = &it->current->second;
+        clean_up.Disable();
+        return &it->property;
+      },
+      result);
 }
 
-mgp_properties_iterator *mgp_vertex_iter_properties(const mgp_vertex *v, mgp_memory *memory) {
+mgp_error mgp_vertex_get_id(const mgp_vertex *v, mgp_vertex_id *result) {
+  return WrapExceptions([v] { return mgp_vertex_id{.as_int = v->impl.Gid().AsInt()}; }, result);
+}
+
+mgp_error mgp_vertex_copy(const mgp_vertex *v, mgp_memory *memory, mgp_vertex **result) {
+  return WrapExceptions([v, memory] { return NewRawMgpObject<mgp_vertex>(memory, *v); }, result);
+}
+
+void mgp_vertex_destroy(mgp_vertex *v) { DeleteRawMgpObject(v); }
+
+mgp_error mgp_vertex_equal(const mgp_vertex *v1, const mgp_vertex *v2, int *result) {
+  // NOLINTNEXTLINE(clang-diagnostic-unevaluated-expression)
+  static_assert(noexcept(*result = *v1 == *v2 ? 1 : 0));
+  *result = *v1 == *v2 ? 1 : 0;
+  return MGP_ERROR_NO_ERROR;
+}
+
+mgp_error mgp_vertex_labels_count(const mgp_vertex *v, size_t *result) {
+  return WrapExceptions(
+      [v]() -> size_t {
+        auto maybe_labels = v->impl.Labels(v->graph->view);
+        if (maybe_labels.HasError()) {
+          switch (maybe_labels.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get the labels of nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting vertex labels.");
+          }
+        }
+        return maybe_labels->size();
+      },
+      result);
+}
+
+mgp_error mgp_vertex_label_at(const mgp_vertex *v, size_t i, mgp_label *result) {
+  return WrapExceptions(
+      [v, i]() -> const char * {
+        // TODO: Maybe it's worth caching this in mgp_vertex.
+        auto maybe_labels = v->impl.Labels(v->graph->view);
+        if (maybe_labels.HasError()) {
+          switch (maybe_labels.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get a label of nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting a label of a vertex.");
+          }
+        }
+        if (i >= maybe_labels->size()) {
+          throw std::out_of_range("Label cannot be retrieved, because index exceeds the number of labels!");
+        }
+        const auto &label = (*maybe_labels)[i];
+        static_assert(std::is_lvalue_reference_v<decltype(v->graph->impl->LabelToName(label))>,
+                      "Expected LabelToName to return a pointer or reference, so we "
+                      "don't have to take a copy and manage memory.");
+        const auto &name = v->graph->impl->LabelToName(label);
+        return name.c_str();
+      },
+      &result->name);
+}
+
+mgp_error mgp_vertex_has_label_named(const mgp_vertex *v, const char *name, int *result) {
+  return WrapExceptions(
+      [v, name] {
+        storage::LabelId label;
+        label = v->graph->impl->NameToLabel(name);
+
+        auto maybe_has_label = v->impl.HasLabel(v->graph->view, label);
+        if (maybe_has_label.HasError()) {
+          switch (maybe_has_label.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot check the existence of a label on nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when checking the existence of a label on a vertex.");
+          }
+        }
+        return *maybe_has_label;
+      },
+      result);
+}
+
+mgp_error mgp_vertex_has_label(const mgp_vertex *v, mgp_label label, int *result) {
+  return mgp_vertex_has_label_named(v, label.name, result);
+}
+
+mgp_error mgp_vertex_get_property(const mgp_vertex *v, const char *name, mgp_memory *memory, mgp_value **result) {
+  return WrapExceptions(
+      [v, name, memory]() -> mgp_value * {
+        const auto &key = v->graph->impl->NameToProperty(name);
+        auto maybe_prop = v->impl.GetProperty(v->graph->view, key);
+        if (maybe_prop.HasError()) {
+          switch (maybe_prop.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get a property of nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting a property of a vertex.");
+          }
+        }
+        return NewRawMgpObject<mgp_value>(memory, std::move(*maybe_prop));
+      },
+      result);
+}
+
+mgp_error mgp_vertex_iter_properties(const mgp_vertex *v, mgp_memory *memory, mgp_properties_iterator **result) {
   // NOTE: This copies the whole properties into the iterator.
   // TODO: Think of a good way to avoid the copy which doesn't just rely on some
   // assumption that storage may return a pointer to the property store. This
   // will probably require a different API in storage.
-  try {
-    auto maybe_props = v->impl.Properties(v->graph->view);
-    if (maybe_props.HasError()) {
-      switch (maybe_props.GetError()) {
-        case storage::Error::DELETED_OBJECT:
-        case storage::Error::NONEXISTENT_OBJECT:
-          // Treat deleted/nonexistent vertex as having no properties.
-          return new_mgp_object<mgp_properties_iterator>(memory, v->graph);
-        case storage::Error::PROPERTIES_DISABLED:
-        case storage::Error::VERTEX_HAS_EDGES:
-        case storage::Error::SERIALIZATION_ERROR:
-          spdlog::error("Unexpected error when getting vertex properties");
-          return nullptr;
-      }
-    }
-    return new_mgp_object<mgp_properties_iterator>(memory, v->graph, std::move(*maybe_props));
-  } catch (...) {
-    // Since we are copying stuff, we may get std::bad_alloc. Hopefully, no
-    // other exceptions are possible, but catch them all just in case.
-    return nullptr;
-  }
+  return WrapExceptions(
+      [v, memory] {
+        auto maybe_props = v->impl.Properties(v->graph->view);
+        if (maybe_props.HasError()) {
+          switch (maybe_props.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get the properties of nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting the properties of a vertex.");
+          }
+        }
+        return NewRawMgpObject<mgp_properties_iterator>(memory, v->graph, std::move(*maybe_props));
+      },
+      result);
 }
 
-void mgp_edges_iterator_destroy(mgp_edges_iterator *it) { delete_mgp_object(it); }
+void mgp_edges_iterator_destroy(mgp_edges_iterator *it) { DeleteRawMgpObject(it); }
 
-mgp_edges_iterator *mgp_vertex_iter_in_edges(const mgp_vertex *v, mgp_memory *memory) {
-  auto *it = new_mgp_object<mgp_edges_iterator>(memory, *v);
-  if (!it) return nullptr;
-  try {
-    auto maybe_edges = v->impl.InEdges(v->graph->view);
-    if (maybe_edges.HasError()) {
-      switch (maybe_edges.GetError()) {
-        case storage::Error::DELETED_OBJECT:
-        case storage::Error::NONEXISTENT_OBJECT:
-          // Treat deleted/nonexistent vertex as having no edges.
-          return it;
-        case storage::Error::PROPERTIES_DISABLED:
-        case storage::Error::VERTEX_HAS_EDGES:
-        case storage::Error::SERIALIZATION_ERROR:
-          spdlog::error("Unexpected error when getting in edges");
-          mgp_edges_iterator_destroy(it);
-          return nullptr;
-      }
-    }
-    it->in.emplace(std::move(*maybe_edges));
-    it->in_it.emplace(it->in->begin());
-    if (*it->in_it != it->in->end()) {
-      it->current_e.emplace(**it->in_it, v->graph, it->GetMemoryResource());
-    }
-  } catch (...) {
-    // We are probably copying edges, and that may throw std::bad_alloc.
-    mgp_edges_iterator_destroy(it);
-    return nullptr;
-  }
-  return it;
+mgp_error mgp_vertex_iter_in_edges(const mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) {
+  return WrapExceptions(
+      [v, memory] {
+        auto it = NewMgpObject<mgp_edges_iterator>(memory, *v);
+        MG_ASSERT(it != nullptr);
+
+        auto maybe_edges = v->impl.InEdges(v->graph->view);
+        if (maybe_edges.HasError()) {
+          switch (maybe_edges.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get the inbound edges of nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting the inbound edges of a vertex.");
+          }
+        }
+        it->in.emplace(std::move(*maybe_edges));
+        it->in_it.emplace(it->in->begin());
+        if (*it->in_it != it->in->end()) {
+          it->current_e.emplace(**it->in_it, v->graph, it->GetMemoryResource());
+        }
+
+        return it.release();
+      },
+      result);
 }
 
-mgp_edges_iterator *mgp_vertex_iter_out_edges(const mgp_vertex *v, mgp_memory *memory) {
-  auto *it = new_mgp_object<mgp_edges_iterator>(memory, *v);
-  if (!it) return nullptr;
-  try {
-    auto maybe_edges = v->impl.OutEdges(v->graph->view);
-    if (maybe_edges.HasError()) {
-      switch (maybe_edges.GetError()) {
-        case storage::Error::DELETED_OBJECT:
-        case storage::Error::NONEXISTENT_OBJECT:
-          // Treat deleted/nonexistent vertex as having no edges.
-          return it;
-        case storage::Error::PROPERTIES_DISABLED:
-        case storage::Error::VERTEX_HAS_EDGES:
-        case storage::Error::SERIALIZATION_ERROR:
-          spdlog::error("Unexpected error when getting out edges");
-          mgp_edges_iterator_destroy(it);
-          return nullptr;
-      }
-    }
-    it->out.emplace(std::move(*maybe_edges));
-    it->out_it.emplace(it->out->begin());
-    if (*it->out_it != it->out->end()) {
-      it->current_e.emplace(**it->out_it, v->graph, it->GetMemoryResource());
-    }
-  } catch (...) {
-    // We are probably copying edges, and that may throw std::bad_alloc.
-    mgp_edges_iterator_destroy(it);
-    return nullptr;
-  }
-  return it;
+mgp_error mgp_vertex_iter_out_edges(const mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) {
+  return WrapExceptions(
+      [v, memory] {
+        auto it = NewMgpObject<mgp_edges_iterator>(memory, *v);
+        MG_ASSERT(it != nullptr);
+
+        auto maybe_edges = v->impl.OutEdges(v->graph->view);
+        if (maybe_edges.HasError()) {
+          switch (maybe_edges.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get the outbound edges of nonexistent vertex"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting the outbound edges of a vertex.");
+          }
+        }
+        it->out.emplace(std::move(*maybe_edges));
+        it->out_it.emplace(it->out->begin());
+        if (*it->out_it != it->out->end()) {
+          it->current_e.emplace(**it->out_it, v->graph, it->GetMemoryResource());
+        }
+
+        return it.release();
+      },
+      result);
 }
 
-const mgp_edge *mgp_edges_iterator_get(const mgp_edges_iterator *it) {
-  if (it->current_e) return &*it->current_e;
-  return nullptr;
+mgp_error mgp_edges_iterator_get(const mgp_edges_iterator *it, const mgp_edge **result) {
+  return WrapExceptions(
+      [it]() -> const mgp_edge * {
+        if (it->current_e.has_value()) {
+          return &*it->current_e;
+        }
+        return nullptr;
+      },
+      result);
 }
 
-const mgp_edge *mgp_edges_iterator_next(mgp_edges_iterator *it) {
-  if (!it->in && !it->out) return nullptr;
-  auto next = [&](auto *impl_it, const auto &end) -> const mgp_edge * {
-    if (*impl_it == end) {
-      MG_ASSERT(!it->current_e,
-                "Iteration is already done, so it->current_e "
-                "should have been set to std::nullopt");
-      return nullptr;
-    }
-    if (++(*impl_it) == end) {
-      it->current_e = std::nullopt;
-      return nullptr;
-    }
-    it->current_e.emplace(**impl_it, it->source_vertex.graph, it->GetMemoryResource());
-    return &*it->current_e;
-  };
-  try {
-    if (it->in_it) {
-      return next(&*it->in_it, it->in->end());
-    } else {
-      return next(&*it->out_it, it->out->end());
-    }
-  } catch (...) {
-    // Just to be sure that operator++ or anything else has thrown something.
-    it->current_e = std::nullopt;
-    return nullptr;
-  }
+mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, const mgp_edge **result) {
+  return WrapExceptions(
+      [it] {
+        MG_ASSERT(it->in || it->out);
+        auto next = [&](auto *impl_it, const auto &end) -> const mgp_edge * {
+          if (*impl_it == end) {
+            MG_ASSERT(!it->current_e,
+                      "Iteration is already done, so it->current_e "
+                      "should have been set to std::nullopt");
+            return nullptr;
+          }
+          if (++(*impl_it) == end) {
+            it->current_e = std::nullopt;
+            return nullptr;
+          }
+          it->current_e.emplace(**impl_it, it->source_vertex.graph, it->GetMemoryResource());
+          return &*it->current_e;
+        };
+        if (it->in_it) {
+          return next(&*it->in_it, it->in->end());
+        }
+        return next(&*it->out_it, it->out->end());
+      },
+      result);
 }
 
-mgp_edge_id mgp_edge_get_id(const mgp_edge *e) { return mgp_edge_id{.as_int = e->impl.Gid().AsInt()}; }
-
-mgp_edge *mgp_edge_copy(const mgp_edge *v, mgp_memory *memory) {
-  return new_mgp_object<mgp_edge>(memory, v->impl, v->from.graph);
+mgp_error mgp_edge_get_id(const mgp_edge *e, mgp_edge_id *result) {
+  return WrapExceptions([e] { return mgp_edge_id{.as_int = e->impl.Gid().AsInt()}; }, result);
 }
 
-void mgp_edge_destroy(mgp_edge *e) { delete_mgp_object(e); }
-
-int mgp_edge_equal(const struct mgp_edge *e1, const struct mgp_edge *e2) { return e1->impl == e2->impl ? 1 : 0; }
-
-mgp_edge_type mgp_edge_get_type(const mgp_edge *e) {
-  const auto &name = e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType());
-  static_assert(std::is_lvalue_reference_v<decltype(e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType()))>,
-                "Expected EdgeTypeToName to return a pointer or reference, so we "
-                "don't have to take a copy and manage memory.");
-  return mgp_edge_type{name.c_str()};
+mgp_error mgp_edge_copy(const mgp_edge *e, mgp_memory *memory, mgp_edge **result) {
+  return WrapExceptions([e, memory] { return NewRawMgpObject<mgp_edge>(memory, e->impl, e->from.graph); }, result);
 }
 
-const mgp_vertex *mgp_edge_get_from(const mgp_edge *e) { return &e->from; }
+void mgp_edge_destroy(mgp_edge *e) { DeleteRawMgpObject(e); }
 
-const mgp_vertex *mgp_edge_get_to(const mgp_edge *e) { return &e->to; }
-
-mgp_value *mgp_edge_get_property(const mgp_edge *e, const char *name, mgp_memory *memory) {
-  try {
-    const auto &key = e->from.graph->impl->NameToProperty(name);
-    auto view = e->from.graph->view;
-    auto maybe_prop = e->impl.GetProperty(view, key);
-    if (maybe_prop.HasError()) {
-      switch (maybe_prop.GetError()) {
-        case storage::Error::DELETED_OBJECT:
-        case storage::Error::NONEXISTENT_OBJECT:
-          // Treat deleted/nonexistent edge as having no properties.
-          return new_mgp_object<mgp_value>(memory);
-        case storage::Error::PROPERTIES_DISABLED:
-        case storage::Error::VERTEX_HAS_EDGES:
-        case storage::Error::SERIALIZATION_ERROR:
-          spdlog::error("Unexpected error when getting edge property");
-          return nullptr;
-      }
-    }
-    return new_mgp_object<mgp_value>(memory, std::move(*maybe_prop));
-  } catch (...) {
-    // In case NameToProperty or GetProperty throw an exception, most likely
-    // std::bad_alloc.
-    return nullptr;
-  }
+mgp_error mgp_edge_equal(const struct mgp_edge *e1, const struct mgp_edge *e2, int *result) {
+  // NOLINTNEXTLINE(clang-diagnostic-unevaluated-expression)
+  static_assert(noexcept(*result = *e1 == *e2 ? 1 : 0));
+  *result = *e1 == *e2 ? 1 : 0;
+  return MGP_ERROR_NO_ERROR;
 }
 
-mgp_properties_iterator *mgp_edge_iter_properties(const mgp_edge *e, mgp_memory *memory) {
+mgp_error mgp_edge_get_type(const mgp_edge *e, mgp_edge_type *result) {
+  return WrapExceptions(
+      [e] {
+        const auto &name = e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType());
+        static_assert(std::is_lvalue_reference_v<decltype(e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType()))>,
+                      "Expected EdgeTypeToName to return a pointer or reference, so we "
+                      "don't have to take a copy and manage memory.");
+        return name.c_str();
+      },
+      &result->name);
+}
+
+mgp_error mgp_edge_get_from(const mgp_edge *e, const mgp_vertex **result) {
+  *result = &e->from;
+  return MGP_ERROR_NO_ERROR;
+}
+
+mgp_error mgp_edge_get_to(const mgp_edge *e, const mgp_vertex **result) {
+  *result = &e->to;
+  return MGP_ERROR_NO_ERROR;
+}
+
+mgp_error mgp_edge_get_property(const mgp_edge *e, const char *name, mgp_memory *memory, mgp_value **result) {
+  return WrapExceptions(
+      [e, name, memory] {
+        const auto &key = e->from.graph->impl->NameToProperty(name);
+        auto view = e->from.graph->view;
+        auto maybe_prop = e->impl.GetProperty(view, key);
+        if (maybe_prop.HasError()) {
+          switch (maybe_prop.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get a property of nonexistent edge"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting a property of an edge.");
+          }
+        }
+        return NewRawMgpObject<mgp_value>(memory, std::move(*maybe_prop));
+      },
+      result);
+}
+
+mgp_error mgp_edge_iter_properties(const mgp_edge *e, mgp_memory *memory, mgp_properties_iterator **result) {
   // NOTE: This copies the whole properties into iterator.
   // TODO: Think of a good way to avoid the copy which doesn't just rely on some
   // assumption that storage may return a pointer to the property store. This
   // will probably require a different API in storage.
-  try {
-    auto view = e->from.graph->view;
-    auto maybe_props = e->impl.Properties(view);
-    if (maybe_props.HasError()) {
-      switch (maybe_props.GetError()) {
-        case storage::Error::DELETED_OBJECT:
-        case storage::Error::NONEXISTENT_OBJECT:
-          // Treat deleted/nonexistent edge as having no properties.
-          return new_mgp_object<mgp_properties_iterator>(memory, e->from.graph);
-        case storage::Error::PROPERTIES_DISABLED:
-        case storage::Error::VERTEX_HAS_EDGES:
-        case storage::Error::SERIALIZATION_ERROR:
-          spdlog::error("Unexpected error when getting edge properties");
+  return WrapExceptions(
+      [e, memory] {
+        auto view = e->from.graph->view;
+        auto maybe_props = e->impl.Properties(view);
+        if (maybe_props.HasError()) {
+          switch (maybe_props.GetError()) {
+            case storage::Error::DELETED_OBJECT:
+            case storage::Error::NONEXISTENT_OBJECT:
+              throw NonexistentObjectException{"Cannot get the properties of nonexistent edge"};
+            case storage::Error::PROPERTIES_DISABLED:
+            case storage::Error::VERTEX_HAS_EDGES:
+            case storage::Error::SERIALIZATION_ERROR:
+              MG_ASSERT(false, "Unexpected error when getting the properties of an edge.");
+          }
+        }
+        return NewRawMgpObject<mgp_properties_iterator>(memory, e->from.graph, std::move(*maybe_props));
+      },
+      result);
+}
+
+mgp_error mgp_graph_get_vertex_by_id(const mgp_graph *graph, mgp_vertex_id id, mgp_memory *memory,
+                                     mgp_vertex **result) {
+  return WrapExceptions(
+      [graph, id, memory]() -> mgp_vertex * {
+        auto maybe_vertex = graph->impl->FindVertex(storage::Gid::FromInt(id.as_int), graph->view);
+        if (maybe_vertex) {
+          return NewRawMgpObject<mgp_vertex>(memory, *maybe_vertex, graph);
+        }
+        return nullptr;
+      },
+      result);
+}
+
+void mgp_vertices_iterator_destroy(mgp_vertices_iterator *it) { DeleteRawMgpObject(it); }
+
+mgp_error mgp_graph_iter_vertices(const mgp_graph *graph, mgp_memory *memory, mgp_vertices_iterator **result) {
+  return WrapExceptions([graph, memory] { return NewRawMgpObject<mgp_vertices_iterator>(memory, graph); }, result);
+}
+
+mgp_error mgp_vertices_iterator_get(const mgp_vertices_iterator *it, const mgp_vertex **result) {
+  return WrapExceptions(
+      [it]() -> const mgp_vertex * {
+        if (it->current_v.has_value()) {
+          return &*it->current_v;
+        }
+        return nullptr;
+      },
+      result);
+}
+
+mgp_error mgp_vertices_iterator_next(mgp_vertices_iterator *it, const mgp_vertex **result) {
+  return WrapExceptions(
+      [it]() -> const mgp_vertex * {
+        if (it->current_it == it->vertices.end()) {
+          MG_ASSERT(!it->current_v,
+                    "Iteration is already done, so it->current_v "
+                    "should have been set to std::nullopt");
           return nullptr;
-      }
-    }
-    return new_mgp_object<mgp_properties_iterator>(memory, e->from.graph, std::move(*maybe_props));
-  } catch (...) {
-    // Since we are copying stuff, we may get std::bad_alloc. Hopefully, no
-    // other exceptions are possible, but catch them all just in case.
-    return nullptr;
-  }
-}
-
-mgp_vertex *mgp_graph_get_vertex_by_id(const mgp_graph *graph, mgp_vertex_id id, mgp_memory *memory) {
-  auto maybe_vertex = graph->impl->FindVertex(storage::Gid::FromInt(id.as_int), graph->view);
-  if (maybe_vertex) return new_mgp_object<mgp_vertex>(memory, *maybe_vertex, graph);
-  return nullptr;
-}
-
-void mgp_vertices_iterator_destroy(mgp_vertices_iterator *it) { delete_mgp_object(it); }
-
-mgp_vertices_iterator *mgp_graph_iter_vertices(const mgp_graph *graph, mgp_memory *memory) {
-  try {
-    return new_mgp_object<mgp_vertices_iterator>(memory, graph);
-  } catch (...) {
-    return nullptr;
-  }
-}
-
-const mgp_vertex *mgp_vertices_iterator_get(const mgp_vertices_iterator *it) {
-  if (it->current_v) return &*it->current_v;
-  return nullptr;
-}
-
-const mgp_vertex *mgp_vertices_iterator_next(mgp_vertices_iterator *it) {
-  try {
-    if (it->current_it == it->vertices.end()) {
-      MG_ASSERT(!it->current_v,
-                "Iteration is already done, so it->current_v "
-                "should have been set to std::nullopt");
-      return nullptr;
-    }
-    if (++it->current_it == it->vertices.end()) {
-      it->current_v = std::nullopt;
-      return nullptr;
-    }
-    it->current_v.emplace(*it->current_it, it->graph, it->GetMemoryResource());
-    return &*it->current_v;
-  } catch (...) {
-    // VerticesIterable::Iterator::operator++ may throw
-    it->current_v = std::nullopt;
-    return nullptr;
-  }
+        }
+        if (++it->current_it == it->vertices.end()) {
+          it->current_v = std::nullopt;
+          return nullptr;
+        }
+        utils::OnScopeExit clean_up([it] { it->current_v = std::nullopt; });
+        it->current_v.emplace(*it->current_it, it->graph, it->GetMemoryResource());
+        clean_up.Disable();
+        return &*it->current_v;
+      },
+      result);
 }
 
 /// Type System
@@ -1187,160 +1396,133 @@ const mgp_vertex *mgp_vertices_iterator_next(mgp_vertices_iterator *it) {
 /// allocations done for types.
 
 namespace {
-void NoOpCypherTypeDeleter(CypherType *) {}
+void NoOpCypherTypeDeleter(CypherType * /*type*/) {}
 }  // namespace
 
-const mgp_type *mgp_type_any() {
-  static AnyType impl;
-  static mgp_type any_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &any_type;
-}
-
-const mgp_type *mgp_type_bool() {
-  static BoolType impl;
-  static mgp_type bool_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &bool_type;
-}
-
-const mgp_type *mgp_type_string() {
-  static StringType impl;
-  static mgp_type string_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &string_type;
-}
-
-const mgp_type *mgp_type_int() {
-  static IntType impl;
-  static mgp_type int_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &int_type;
-}
-
-const mgp_type *mgp_type_float() {
-  static FloatType impl;
-  static mgp_type float_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &float_type;
-}
-
-const mgp_type *mgp_type_number() {
-  static NumberType impl;
-  static mgp_type number_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &number_type;
-}
-
-const mgp_type *mgp_type_map() {
-  static MapType impl;
-  static mgp_type map_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &map_type;
-}
-
-const mgp_type *mgp_type_node() {
-  static NodeType impl;
-  static mgp_type node_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &node_type;
-}
-
-const mgp_type *mgp_type_relationship() {
-  static RelationshipType impl;
-  static mgp_type relationship_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &relationship_type;
-}
-
-const mgp_type *mgp_type_path() {
-  static PathType impl;
-  static mgp_type path_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)};
-  return &path_type;
-}
-
-const mgp_type *mgp_type_list(const mgp_type *type) {
-  if (!type) return nullptr;
-  // Maps `type` to corresponding instance of ListType.
-  static utils::pmr::map<const mgp_type *, mgp_type> list_types(utils::NewDeleteResource());
-  static utils::SpinLock lock;
-  std::lock_guard<utils::SpinLock> guard(lock);
-  auto found_it = list_types.find(type);
-  if (found_it != list_types.end()) return &found_it->second;
-  try {
-    auto alloc = list_types.get_allocator();
-    CypherTypePtr impl(
-        alloc.new_object<ListType>(
-            // Just obtain the pointer to original impl, don't own it.
-            CypherTypePtr(type->impl.get(), NoOpCypherTypeDeleter), alloc.GetMemoryResource()),
-        [alloc](CypherType *base_ptr) mutable { alloc.delete_object(static_cast<ListType *>(base_ptr)); });
-    return &list_types.emplace(type, mgp_type{std::move(impl)}).first->second;
-  } catch (const std::bad_alloc &) {
-    return nullptr;
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define DEFINE_MGP_TYPE_GETTER(cypher_type_name, mgp_type_name)                            \
+  mgp_error mgp_type_##mgp_type_name(const mgp_type **result) {                            \
+    return WrapExceptions(                                                                 \
+        [] {                                                                               \
+          static cypher_type_name##Type impl;                                              \
+          static mgp_type mgp_type_name_type{CypherTypePtr(&impl, NoOpCypherTypeDeleter)}; \
+          return &mgp_type_name_type;                                                      \
+        },                                                                                 \
+        result);                                                                           \
   }
+
+DEFINE_MGP_TYPE_GETTER(Any, any);
+DEFINE_MGP_TYPE_GETTER(Bool, bool);
+DEFINE_MGP_TYPE_GETTER(String, string);
+DEFINE_MGP_TYPE_GETTER(Int, int);
+DEFINE_MGP_TYPE_GETTER(Float, float);
+DEFINE_MGP_TYPE_GETTER(Number, number);
+DEFINE_MGP_TYPE_GETTER(Map, map);
+DEFINE_MGP_TYPE_GETTER(Node, node);
+DEFINE_MGP_TYPE_GETTER(Relationship, relationship);
+DEFINE_MGP_TYPE_GETTER(Path, path);
+
+mgp_error mgp_type_list(const mgp_type *type, const mgp_type **result) {
+  return WrapExceptions(
+      [type] {
+        // Maps `type` to corresponding instance of ListType.
+        static utils::pmr::map<const mgp_type *, mgp_type> gListTypes(utils::NewDeleteResource());
+        static utils::SpinLock lock;
+        std::lock_guard<utils::SpinLock> guard(lock);
+        auto found_it = gListTypes.find(type);
+        if (found_it != gListTypes.end()) {
+          return &found_it->second;
+        }
+        auto alloc = gListTypes.get_allocator();
+        CypherTypePtr impl(
+            alloc.new_object<ListType>(
+                // Just obtain the pointer to original impl, don't own it.
+                CypherTypePtr(type->impl.get(), NoOpCypherTypeDeleter), alloc.GetMemoryResource()),
+            [alloc](CypherType *base_ptr) mutable { alloc.delete_object(static_cast<ListType *>(base_ptr)); });
+        return &gListTypes.emplace(type, mgp_type{std::move(impl)}).first->second;
+      },
+      result);
 }
 
-const mgp_type *mgp_type_nullable(const mgp_type *type) {
-  if (!type) return nullptr;
-  // Maps `type` to corresponding instance of NullableType.
-  static utils::pmr::map<const mgp_type *, mgp_type> gNullableTypes(utils::NewDeleteResource());
-  static utils::SpinLock lock;
-  std::lock_guard<utils::SpinLock> guard(lock);
-  auto found_it = gNullableTypes.find(type);
-  if (found_it != gNullableTypes.end()) return &found_it->second;
-  try {
-    auto alloc = gNullableTypes.get_allocator();
-    auto impl = NullableType::Create(CypherTypePtr(type->impl.get(), NoOpCypherTypeDeleter), alloc.GetMemoryResource());
-    return &gNullableTypes.emplace(type, mgp_type{std::move(impl)}).first->second;
-  } catch (const std::bad_alloc &) {
-    return nullptr;
-  }
+mgp_error mgp_type_nullable(const mgp_type *type, const mgp_type **result) {
+  return WrapExceptions(
+      [type] {
+        // Maps `type` to corresponding instance of NullableType.
+        static utils::pmr::map<const mgp_type *, mgp_type> gNullableTypes(utils::NewDeleteResource());
+        static utils::SpinLock lock;
+        std::lock_guard<utils::SpinLock> guard(lock);
+        auto found_it = gNullableTypes.find(type);
+        if (found_it != gNullableTypes.end()) return &found_it->second;
+
+        auto alloc = gNullableTypes.get_allocator();
+        auto impl =
+            NullableType::Create(CypherTypePtr(type->impl.get(), NoOpCypherTypeDeleter), alloc.GetMemoryResource());
+        return &gNullableTypes.emplace(type, mgp_type{std::move(impl)}).first->second;
+      },
+      result);
 }
 
-mgp_proc *mgp_module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb) {
-  if (!module || !cb) return nullptr;
-  if (!IsValidIdentifierName(name)) return nullptr;
-  if (module->procedures.find(name) != module->procedures.end()) return nullptr;
-  try {
-    auto *memory = module->procedures.get_allocator().GetMemoryResource();
-    // May throw std::bad_alloc, std::length_error
-    return &module->procedures.emplace(name, mgp_proc(name, cb, memory)).first->second;
-  } catch (...) {
-    return nullptr;
-  }
+mgp_error mgp_module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, mgp_proc **result) {
+  return WrapExceptions(
+      [module, name, cb]() -> mgp_proc * {
+        if (!IsValidIdentifierName(name)) {
+          throw std::invalid_argument{fmt::format("Invalid procedure name: {}", name)};
+        }
+        if (module->procedures.find(name) != module->procedures.end()) {
+          throw std::logic_error{fmt::format("Procedure already exists with name '{}'", name)};
+        };
+
+        auto *memory = module->procedures.get_allocator().GetMemoryResource();
+        // May throw std::bad_alloc, std::length_error
+        return &module->procedures.emplace(name, mgp_proc(name, cb, memory)).first->second;
+      },
+      result);
 }
 
-int mgp_proc_add_arg(mgp_proc *proc, const char *name, const mgp_type *type) {
-  if (!proc || !type) return 0;
-  if (!proc->opt_args.empty()) return 0;
-  if (!IsValidIdentifierName(name)) return 0;
-  try {
+mgp_error mgp_proc_add_arg(mgp_proc *proc, const char *name, const mgp_type *type) {
+  return WrapExceptions([=] {
+    if (!IsValidIdentifierName(name)) {
+      throw std::invalid_argument{fmt::format("Invalid argument name for procedure '{}': {}", proc->name, name)};
+    }
+    if (!proc->opt_args.empty()) {
+      throw std::logic_error{fmt::format(
+          "Cannot add required argument '{}' to procedure '{}' after adding any optional one", name, proc->name)};
+    }
     proc->args.emplace_back(name, type->impl.get());
-    return 1;
-  } catch (...) {
-    return 0;
-  }
+  });
 }
 
-int mgp_proc_add_opt_arg(mgp_proc *proc, const char *name, const mgp_type *type, const mgp_value *default_value) {
-  if (!proc || !type || !default_value) return 0;
-  if (!IsValidIdentifierName(name)) return 0;
-  switch (mgp_value_get_type(default_value)) {
-    case MGP_VALUE_TYPE_VERTEX:
-    case MGP_VALUE_TYPE_EDGE:
-    case MGP_VALUE_TYPE_PATH:
-      // default_value must not be a graph element.
-      return 0;
-    case MGP_VALUE_TYPE_NULL:
-    case MGP_VALUE_TYPE_BOOL:
-    case MGP_VALUE_TYPE_INT:
-    case MGP_VALUE_TYPE_DOUBLE:
-    case MGP_VALUE_TYPE_STRING:
-    case MGP_VALUE_TYPE_LIST:
-    case MGP_VALUE_TYPE_MAP:
-      break;
-  }
-  // Default value must be of required `type`.
-  if (!type->impl->SatisfiesType(*default_value)) return 0;
-  auto *memory = proc->opt_args.get_allocator().GetMemoryResource();
-  try {
+mgp_error mgp_proc_add_opt_arg(mgp_proc *proc, const char *name, const mgp_type *type, const mgp_value *default_value) {
+  return WrapExceptions([=] {
+    if (!IsValidIdentifierName(name)) {
+      throw std::invalid_argument{fmt::format("Invalid argument name for procedure '{}': {}", proc->name, name)};
+    }
+    switch (MgpValueGetType(*default_value)) {
+      case MGP_VALUE_TYPE_VERTEX:
+      case MGP_VALUE_TYPE_EDGE:
+      case MGP_VALUE_TYPE_PATH:
+        // default_value must not be a graph element.
+        throw std::out_of_range{fmt::format(
+            "Default value of argument '{}' of procedure '{}' name must not be a graph element!", name, proc->name)};
+      case MGP_VALUE_TYPE_NULL:
+      case MGP_VALUE_TYPE_BOOL:
+      case MGP_VALUE_TYPE_INT:
+      case MGP_VALUE_TYPE_DOUBLE:
+      case MGP_VALUE_TYPE_STRING:
+      case MGP_VALUE_TYPE_LIST:
+      case MGP_VALUE_TYPE_MAP:
+        break;
+    }
+    // Default value must be of required `type`.
+    if (!type->impl->SatisfiesType(*default_value)) {
+      throw std::logic_error{
+          fmt::format("The default value of argument '{}' for procedure '{}' doesn't satisfy type '{}'", name,
+                      proc->name, type->impl->GetPresentableName())};
+    }
+    auto *memory = proc->opt_args.get_allocator().GetMemoryResource();
     proc->opt_args.emplace_back(utils::pmr::string(name, memory), type->impl.get(),
                                 ToTypedValue(*default_value, memory));
-    return 1;
-  } catch (...) {
-    return 0;
-  }
+  });
 }
 
 namespace {
@@ -1349,39 +1531,42 @@ template <typename T>
 concept ModuleProperties = utils::SameAsAnyOf<T, mgp_proc, mgp_trans>;
 
 template <ModuleProperties T>
-bool AddResultToProp(T *prop, const char *name, const mgp_type *type, bool is_deprecated) {
-  if (!prop || !type) return false;
-  if (!IsValidIdentifierName(name)) return false;
-  if (prop->results.find(name) != prop->results.end()) return false;
-  try {
+mgp_error AddResultToProp(T *prop, const char *name, const mgp_type *type, bool is_deprecated) noexcept {
+  return WrapExceptions([=] {
+    if (!IsValidIdentifierName(name)) {
+      throw std::invalid_argument{fmt::format("Invalid result name for procedure '{}': {}", prop->name, name)};
+    }
+    if (prop->results.find(name) != prop->results.end()) {
+      throw std::logic_error{fmt::format("Result already exists with name '{}' for procedure '{}'", name, prop->name)};
+    };
     auto *memory = prop->results.get_allocator().GetMemoryResource();
     prop->results.emplace(utils::pmr::string(name, memory), std::make_pair(type->impl.get(), is_deprecated));
-    return true;
-  } catch (...) {
-    return false;
-  }
+  });
 }
 
 }  // namespace
 
-int mgp_proc_add_result(mgp_proc *proc, const char *name, const mgp_type *type) {
+mgp_error mgp_proc_add_result(mgp_proc *proc, const char *name, const mgp_type *type) {
   return AddResultToProp(proc, name, type, false);
 }
 
-bool MgpTransAddFixedResult(mgp_trans *trans) {
-  if (int err = AddResultToProp(trans, "query", mgp_type_string(), false); err != 1) {
+mgp_error MgpTransAddFixedResult(mgp_trans *trans) noexcept {
+  if (const auto err = AddResultToProp(trans, "query", Call<const mgp_type *>(mgp_type_string), false);
+      err != MGP_ERROR_NO_ERROR) {
     return err;
   }
-  return AddResultToProp(trans, "parameters", mgp_type_nullable(mgp_type_map()), false);
+  return AddResultToProp(trans, "parameters",
+                         Call<const mgp_type *>(mgp_type_nullable, Call<const mgp_type *>(mgp_type_map)), false);
 }
 
-int mgp_proc_add_deprecated_result(mgp_proc *proc, const char *name, const mgp_type *type) {
+mgp_error mgp_proc_add_deprecated_result(mgp_proc *proc, const char *name, const mgp_type *type) {
   return AddResultToProp(proc, name, type, true);
 }
 
 int mgp_must_abort(const mgp_graph *graph) {
   MG_ASSERT(graph->ctx);
-  return query::MustAbort(*graph->ctx);
+  static_assert(noexcept(query::MustAbort(*graph->ctx)));
+  return query::MustAbort(*graph->ctx) ? 1 : 0;
 }
 
 namespace query::procedure {
@@ -1453,34 +1638,56 @@ bool IsValidIdentifierName(const char *name) {
 
 }  // namespace query::procedure
 
-const char *mgp_message_payload(const mgp_message *message) { return message->msg->Payload().data(); }
-
-size_t mgp_message_payload_size(const mgp_message *message) { return message->msg->Payload().size(); }
-
-const char *mgp_message_topic_name(const mgp_message *message) { return message->msg->TopicName().data(); }
-
-const char *mgp_message_key(const mgp_message *message) { return message->msg->Key().data(); }
-
-size_t mgp_message_key_size(const struct mgp_message *message) { return message->msg->Key().size(); }
-
-int64_t mgp_message_timestamp(const mgp_message *message) { return message->msg->Timestamp(); }
-
-size_t mgp_messages_size(const mgp_messages *messages) { return messages->messages.size(); }
-
-const mgp_message *mgp_messages_at(const mgp_messages *messages, size_t index) {
-  return index >= mgp_messages_size(messages) ? nullptr : &messages->messages[index];
+mgp_error mgp_message_payload(const mgp_message *message, const char **result) {
+  return WrapExceptions([message] { return message->msg->Payload().data(); }, result);
 }
 
-int mgp_module_add_transformation(mgp_module *module, const char *name, mgp_trans_cb cb) {
-  if (!module || !cb) return 0;
-  if (!IsValidIdentifierName(name)) return 0;
-  if (module->transformations.find(name) != module->transformations.end()) return 0;
-  try {
+mgp_error mgp_message_payload_size(const mgp_message *message, size_t *result) {
+  return WrapExceptions([message] { return message->msg->Payload().size(); }, result);
+}
+
+mgp_error mgp_message_topic_name(const mgp_message *message, const char **result) {
+  return WrapExceptions([message] { return message->msg->TopicName().data(); }, result);
+}
+
+mgp_error mgp_message_key(const mgp_message *message, const char **result) {
+  return WrapExceptions([message] { return message->msg->Key().data(); }, result);
+}
+
+mgp_error mgp_message_key_size(const struct mgp_message *message, size_t *result) {
+  return WrapExceptions([message] { return message->msg->Key().size(); }, result);
+}
+
+mgp_error mgp_message_timestamp(const mgp_message *message, int64_t *result) {
+  return WrapExceptions([message] { return message->msg->Timestamp(); }, result);
+}
+
+mgp_error mgp_messages_size(const mgp_messages *messages, size_t *result) {
+  static_assert(noexcept(messages->messages.size()));
+  *result = messages->messages.size();
+  return MGP_ERROR_NO_ERROR;
+}
+
+mgp_error mgp_messages_at(const mgp_messages *messages, size_t index, const mgp_message **result) {
+  return WrapExceptions(
+      [messages, index] {
+        if (index >= Call<size_t>(mgp_messages_size, messages)) {
+          throw std::out_of_range("Message cannot be retrieved, because index exceeds messages' size!");
+        }
+        return &messages->messages[index];
+      },
+      result);
+}
+
+mgp_error mgp_module_add_transformation(mgp_module *module, const char *name, mgp_trans_cb cb) {
+  return WrapExceptions([=] {
+    if (!IsValidIdentifierName(name)) {
+      throw std::invalid_argument{fmt::format("Invalid transformation name: {}", name)};
+    }
+    if (module->transformations.find(name) != module->transformations.end()) {
+      throw std::logic_error{fmt::format("Transformation already exists with name '{}'", name)};
+    };
     auto *memory = module->transformations.get_allocator().GetMemoryResource();
-    // May throw std::bad_alloc, std::length_error
     module->transformations.emplace(name, mgp_trans(name, cb, memory));
-    return 1;
-  } catch (...) {
-    return 0;
-  }
+  });
 }
diff --git a/src/query/procedure/mg_procedure_impl.hpp b/src/query/procedure/mg_procedure_impl.hpp
index 313d581e3..8ea1f7fbd 100644
--- a/src/query/procedure/mg_procedure_impl.hpp
+++ b/src/query/procedure/mg_procedure_impl.hpp
@@ -221,6 +221,9 @@ struct mgp_vertex {
   mgp_vertex &operator=(const mgp_vertex &) = delete;
   mgp_vertex &operator=(mgp_vertex &&) = delete;
 
+  bool operator==(const mgp_vertex &other) const noexcept { return this->impl == other.impl; }
+  bool operator!=(const mgp_vertex &other) const noexcept { return !(*this == other); };
+
   ~mgp_vertex() = default;
 
   utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
@@ -257,9 +260,11 @@ struct mgp_edge {
 
   mgp_edge &operator=(const mgp_edge &) = delete;
   mgp_edge &operator=(mgp_edge &&) = delete;
-
   ~mgp_edge() = default;
 
+  bool operator==(const mgp_edge &other) const noexcept { return this->impl == other.impl; }
+  bool operator!=(const mgp_edge &other) const noexcept { return !(*this == other); };
+
   utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
 
   utils::MemoryResource *memory;
@@ -512,7 +517,7 @@ struct mgp_trans {
   utils::pmr::map<utils::pmr::string, std::pair<const query::procedure::CypherType *, bool>> results;
 };
 
-bool MgpTransAddFixedResult(mgp_trans *trans);
+mgp_error MgpTransAddFixedResult(mgp_trans *trans) noexcept;
 
 struct mgp_module {
   using allocator_type = utils::Allocator<mgp_module>;
diff --git a/src/query/procedure/module.cpp b/src/query/procedure/module.cpp
index 4d4d291f0..d692157ae 100644
--- a/src/query/procedure/module.cpp
+++ b/src/query/procedure/module.cpp
@@ -91,30 +91,40 @@ void RegisterMgLoad(ModuleRegistry *module_registry, utils::RWLock *lock, Builti
     }
     lock->lock_shared();
   };
-  auto load_all_cb = [module_registry, with_unlock_shared](const mgp_list *, const mgp_graph *, mgp_result *,
-                                                           mgp_memory *) {
+  auto load_all_cb = [module_registry, with_unlock_shared](const mgp_list * /*args*/, const mgp_graph * /*graph*/,
+                                                           mgp_result * /*result*/, mgp_memory * /*memory*/) {
     with_unlock_shared([&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
   };
   mgp_proc load_all("load_all", load_all_cb, utils::NewDeleteResource());
   module->AddProcedure("load_all", std::move(load_all));
-  auto load_cb = [module_registry, with_unlock_shared](const mgp_list *args, const mgp_graph *, mgp_result *res,
-                                                       mgp_memory *) {
-    MG_ASSERT(mgp_list_size(args) == 1U, "Should have been type checked already");
-    const mgp_value *arg = mgp_list_at(args, 0);
-    MG_ASSERT(mgp_value_is_string(arg), "Should have been type checked already");
+  auto load_cb = [module_registry, with_unlock_shared](const mgp_list *args, const mgp_graph * /*graph*/,
+                                                       mgp_result *result, mgp_memory * /*memory*/) {
+    MG_ASSERT(Call<size_t>(mgp_list_size, args) == 1U, "Should have been type checked already");
+    const auto *arg = Call<const mgp_value *>(mgp_list_at, args, 0);
+    MG_ASSERT(CallBool(mgp_value_is_string, arg), "Should have been type checked already");
     bool succ = false;
-    with_unlock_shared([&]() { succ = module_registry->LoadOrReloadModuleFromName(mgp_value_get_string(arg)); });
-    if (!succ) mgp_result_set_error_msg(res, "Failed to (re)load the module.");
+    with_unlock_shared([&]() {
+      const char *arg_as_string{nullptr};
+      if (const auto err = mgp_value_get_string(arg, &arg_as_string); err != MGP_ERROR_NO_ERROR) {
+        succ = false;
+      } else {
+        succ = module_registry->LoadOrReloadModuleFromName(arg_as_string);
+      }
+    });
+    if (!succ) {
+      MG_ASSERT(mgp_result_set_error_msg(result, "Failed to (re)load the module.") == MGP_ERROR_NO_ERROR);
+    }
   };
   mgp_proc load("load", load_cb, utils::NewDeleteResource());
-  mgp_proc_add_arg(&load, "module_name", mgp_type_string());
+  MG_ASSERT(mgp_proc_add_arg(&load, "module_name", Call<const mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
   module->AddProcedure("load", std::move(load));
 }
 
 void RegisterMgProcedures(
     // We expect modules to be sorted by name.
     const std::map<std::string, std::unique_ptr<Module>, std::less<>> *all_modules, BuiltinModule *module) {
-  auto procedures_cb = [all_modules](const mgp_list *, const mgp_graph *, mgp_result *result, mgp_memory *memory) {
+  auto procedures_cb = [all_modules](const mgp_list * /*args*/, const mgp_graph * /*graph*/, mgp_result *result,
+                                     mgp_memory *memory) {
     // Iterating over all_modules assumes that the standard mechanism of custom
     // procedure invocations takes the ModuleRegistry::lock_ with READ access.
     // For details on how the invocation is done, take a look at the
@@ -125,43 +135,53 @@ void RegisterMgProcedures(
           std::is_same_v<decltype(module->Procedures()), const std::map<std::string, mgp_proc, std::less<>> *>,
           "Expected module procedures to be sorted by name");
       for (const auto &[proc_name, proc] : *module->Procedures()) {
-        auto *record = mgp_result_new_record(result);
-        if (!record) {
-          mgp_result_set_error_msg(result, "Not enough memory!");
+        mgp_result_record *record{nullptr};
+        if (const auto err = mgp_result_new_record(result, &record); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
+          return;
+        } else if (err != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
           return;
         }
+
         utils::pmr::string full_name(module_name, memory->impl);
         full_name.append(1, '.');
         full_name.append(proc_name);
-        auto *name_value = mgp_value_make_string(full_name.c_str(), memory);
-        if (!name_value) {
-          mgp_result_set_error_msg(result, "Not enough memory!");
+        MgpUniquePtr<mgp_value> name_value{nullptr, mgp_value_destroy};
+        if (const auto err = CreateMgpObject(name_value, mgp_value_make_string, full_name.c_str(), memory);
+            err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
+          return;
+        } else if (err != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
           return;
         }
         std::stringstream ss;
         ss << module_name << ".";
         PrintProcSignature(proc, &ss);
         const auto signature = ss.str();
-        auto *signature_value = mgp_value_make_string(signature.c_str(), memory);
-        if (!signature_value) {
-          mgp_value_destroy(name_value);
-          mgp_result_set_error_msg(result, "Not enough memory!");
+        MgpUniquePtr<mgp_value> signature_value{nullptr, mgp_value_destroy};
+        if (const auto err = CreateMgpObject(signature_value, mgp_value_make_string, full_name.c_str(), memory);
+            err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
+          return;
+        } else if (err != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
           return;
         }
-        int succ1 = mgp_result_record_insert(record, "name", name_value);
-        int succ2 = mgp_result_record_insert(record, "signature", signature_value);
-        mgp_value_destroy(name_value);
-        mgp_value_destroy(signature_value);
-        if (!succ1 || !succ2) {
-          mgp_result_set_error_msg(result, "Unable to set the result!");
+        const auto err1 = mgp_result_record_insert(record, "name", name_value.get());
+        const auto err2 = mgp_result_record_insert(record, "signature", signature_value.get());
+        if (err1 != MGP_ERROR_NO_ERROR || err2 != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unable to set the result!"));
           return;
         }
       }
     }
   };
   mgp_proc procedures("procedures", procedures_cb, utils::NewDeleteResource());
-  mgp_proc_add_result(&procedures, "name", mgp_type_string());
-  mgp_proc_add_result(&procedures, "signature", mgp_type_string());
+  MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<const mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
+  MG_ASSERT(mgp_proc_add_result(&procedures, "signature", Call<const mgp_type *>(mgp_type_string)) ==
+            MGP_ERROR_NO_ERROR);
   module->AddProcedure("procedures", std::move(procedures));
 }
 
@@ -175,32 +195,41 @@ void RegisterMgTransformations(const std::map<std::string, std::unique_ptr<Modul
           std::is_same_v<decltype(module->Transformations()), const std::map<std::string, mgp_trans, std::less<>> *>,
           "Expected module transformations to be sorted by name");
       for (const auto &[trans_name, proc] : *module->Transformations()) {
-        auto *record = mgp_result_new_record(result);
-        if (!record) {
-          mgp_result_set_error_msg(result, "Not enough memory!");
+        mgp_result_record *record{nullptr};
+        if (const auto err = mgp_result_new_record(result, &record); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
+          return;
+        } else if (err != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
           return;
         }
+
         utils::pmr::string full_name(module_name, memory->impl);
         full_name.append(1, '.');
         full_name.append(trans_name);
-        auto *name_value = mgp_value_make_string(full_name.c_str(), memory);
-        if (!name_value) {
-          mgp_result_set_error_msg(result, "Not enough memory!");
+
+        MgpUniquePtr<mgp_value> name_value{nullptr, mgp_value_destroy};
+        if (const auto err = CreateMgpObject(name_value, mgp_value_make_string, full_name.c_str(), memory);
+            err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Not enough memory!"));
+          return;
+        } else if (err != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unexpected error"));
           return;
         }
-        int succ = mgp_result_record_insert(record, "name", name_value);
-        mgp_value_destroy(name_value);
-        if (!succ) {
-          mgp_result_set_error_msg(result, "Unable to set the result!");
+
+        if (const auto err = mgp_result_record_insert(record, "name", name_value.get()); err != MGP_ERROR_NO_ERROR) {
+          static_cast<void>(mgp_result_set_error_msg(result, "Unable to set the result!"));
           return;
         }
       }
     }
   };
   mgp_proc procedures("transformations", procedures_cb, utils::NewDeleteResource());
-  mgp_proc_add_result(&procedures, "name", mgp_type_string());
+  MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<const mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
   module->AddProcedure("transformations", std::move(procedures));
 }
+
 // Run `fun` with `mgp_module *` and `mgp_memory *` arguments. If `fun` returned
 // a `true` value, store the `mgp_module::procedures` and
 // `mgp_module::transformations into `proc_map`. The return value of WithModuleRegistration
@@ -392,11 +421,13 @@ bool PythonModule::Load(const std::filesystem::path &file_path) {
     return false;
   }
   bool succ = true;
-  auto module_cb = [&](auto *module_def, auto *memory) {
+  auto module_cb = [&](auto *module_def, auto * /*memory*/) {
     auto result = ImportPyModule(file_path.stem().c_str(), module_def);
     for (auto &trans : module_def->transformations) {
-      succ = MgpTransAddFixedResult(&trans.second);
-      if (!succ) return result;
+      succ = MgpTransAddFixedResult(&trans.second) == MGP_ERROR_NO_ERROR;
+      if (!succ) {
+        return result;
+      }
     };
     return result;
   };
@@ -578,7 +609,7 @@ void ModuleRegistry::UnloadAllModules() {
   DoUnloadAllModules();
 }
 
-utils::MemoryResource &ModuleRegistry::GetSharedMemoryResource() { return *shared_; }
+utils::MemoryResource &ModuleRegistry::GetSharedMemoryResource() noexcept { return *shared_; }
 
 namespace {
 
diff --git a/src/query/procedure/module.hpp b/src/query/procedure/module.hpp
index 1d05ac160..1542fcda0 100644
--- a/src/query/procedure/module.hpp
+++ b/src/query/procedure/module.hpp
@@ -100,7 +100,7 @@ class ModuleRegistry final {
   void UnloadAllModules();
 
   /// Returns the shared memory allocator used by modules
-  utils::MemoryResource &GetSharedMemoryResource();
+  utils::MemoryResource &GetSharedMemoryResource() noexcept;
 
  private:
   std::vector<std::filesystem::path> modules_dirs_;
diff --git a/src/query/procedure/py_module.cpp b/src/query/procedure/py_module.cpp
index 03b4c089f..0562e0083 100644
--- a/src/query/procedure/py_module.cpp
+++ b/src/query/procedure/py_module.cpp
@@ -4,6 +4,7 @@
 #include <stdexcept>
 #include <string>
 
+#include "query/procedure/mg_procedure_helpers.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
 #include "utils/pmr/vector.hpp"
 
@@ -70,7 +71,7 @@ PyObject *PyVerticesIteratorGet(PyVerticesIterator *self, PyObject *Py_UNUSED(ig
   MG_ASSERT(self->it);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *vertex = mgp_vertices_iterator_get(self->it);
+  const auto *vertex = Call<const mgp_vertex *>(mgp_vertices_iterator_get, self->it);
   if (!vertex) Py_RETURN_NONE;
   return MakePyVertex(*vertex, self->py_graph);
 }
@@ -79,7 +80,7 @@ PyObject *PyVerticesIteratorNext(PyVerticesIterator *self, PyObject *Py_UNUSED(i
   MG_ASSERT(self->it);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *vertex = mgp_vertices_iterator_next(self->it);
+  const auto *vertex = Call<const mgp_vertex *>(mgp_vertices_iterator_next, self->it);
   if (!vertex) Py_RETURN_NONE;
   return MakePyVertex(*vertex, self->py_graph);
 }
@@ -130,7 +131,7 @@ PyObject *PyEdgesIteratorGet(PyEdgesIterator *self, PyObject *Py_UNUSED(ignored)
   MG_ASSERT(self->it);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *edge = mgp_edges_iterator_get(self->it);
+  const auto *edge = Call<const mgp_edge *>(mgp_edges_iterator_get, self->it);
   if (!edge) Py_RETURN_NONE;
   return MakePyEdge(*edge, self->py_graph);
 }
@@ -139,7 +140,7 @@ PyObject *PyEdgesIteratorNext(PyEdgesIterator *self, PyObject *Py_UNUSED(ignored
   MG_ASSERT(self->it);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *edge = mgp_edges_iterator_next(self->it);
+  const auto *edge = Call<const mgp_edge *>(mgp_edges_iterator_next, self->it);
   if (!edge) Py_RETURN_NONE;
   return MakePyEdge(*edge, self->py_graph);
 }
@@ -181,7 +182,7 @@ PyObject *PyGraphGetVertexById(PyGraph *self, PyObject *args) {
   static_assert(std::is_same_v<int64_t, long>);
   int64_t id = 0;
   if (!PyArg_ParseTuple(args, "l", &id)) return nullptr;
-  auto *vertex = mgp_graph_get_vertex_by_id(self->graph, mgp_vertex_id{id}, self->memory);
+  auto *vertex = Call<mgp_vertex *>(mgp_graph_get_vertex_by_id, self->graph, mgp_vertex_id{id}, self->memory);
   if (!vertex) {
     PyErr_SetString(PyExc_IndexError, "Unable to find the vertex with given ID.");
     return nullptr;
@@ -194,7 +195,7 @@ PyObject *PyGraphGetVertexById(PyGraph *self, PyObject *args) {
 PyObject *PyGraphIterVertices(PyGraph *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->graph);
   MG_ASSERT(self->memory);
-  auto *vertices_it = mgp_graph_iter_vertices(self->graph, self->memory);
+  auto *vertices_it = Call<mgp_vertices_iterator *>(mgp_graph_iter_vertices, self->graph, self->memory);
   if (!vertices_it) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_vertices_iterator.");
     return nullptr;
@@ -291,7 +292,7 @@ PyObject *PyQueryProcAddArg(PyQueryProc *self, PyObject *args) {
     return nullptr;
   }
   const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
-  if (!mgp_proc_add_arg(self->proc, name, type)) {
+  if (mgp_proc_add_arg(self->proc, name, type) != MGP_ERROR_NO_ERROR) {
     PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_arg.");
     return nullptr;
   }
@@ -310,7 +311,7 @@ PyObject *PyQueryProcAddOptArg(PyQueryProc *self, PyObject *args) {
   }
   const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
   mgp_memory memory{self->proc->opt_args.get_allocator().GetMemoryResource()};
-  mgp_value *value;
+  mgp_value *value{nullptr};
   try {
     value = PyObjectToMgpValue(py_value, &memory);
   } catch (const std::bad_alloc &e) {
@@ -327,7 +328,7 @@ PyObject *PyQueryProcAddOptArg(PyQueryProc *self, PyObject *args) {
     return nullptr;
   }
   MG_ASSERT(value);
-  if (!mgp_proc_add_opt_arg(self->proc, name, type, value)) {
+  if (mgp_proc_add_opt_arg(self->proc, name, type, value) != MGP_ERROR_NO_ERROR) {
     mgp_value_destroy(value);
     PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_opt_arg.");
     return nullptr;
@@ -346,7 +347,7 @@ PyObject *PyQueryProcAddResult(PyQueryProc *self, PyObject *args) {
     return nullptr;
   }
   const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
-  if (!mgp_proc_add_result(self->proc, name, type)) {
+  if (mgp_proc_add_result(self->proc, name, type) != MGP_ERROR_NO_ERROR) {
     PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_result.");
     return nullptr;
   }
@@ -363,7 +364,7 @@ PyObject *PyQueryProcAddDeprecatedResult(PyQueryProc *self, PyObject *args) {
     return nullptr;
   }
   const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
-  if (!mgp_proc_add_deprecated_result(self->proc, name, type)) {
+  if (const auto err = mgp_proc_add_deprecated_result(self->proc, name, type); err != MGP_ERROR_NO_ERROR) {
     PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_deprecated_result.");
     return nullptr;
   }
@@ -424,8 +425,8 @@ PyObject *PyMessageIsValid(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
 
 PyObject *PyMessageGetPayload(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->message);
-  auto payload_size = mgp_message_payload_size(self->message);
-  const auto *payload = mgp_message_payload(self->message);
+  auto payload_size = Call<size_t>(mgp_message_payload_size, self->message);
+  const auto *payload = Call<const char *>(mgp_message_payload, self->message);
   auto *raw_bytes = PyByteArray_FromStringAndSize(payload, payload_size);
   if (!raw_bytes) {
     PyErr_SetString(PyExc_RuntimeError, "Unable to get raw bytes from payload");
@@ -437,7 +438,7 @@ PyObject *PyMessageGetPayload(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
 PyObject *PyMessageGetTopicName(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->message);
   MG_ASSERT(self->memory);
-  const auto *topic_name = mgp_message_topic_name(self->message);
+  const auto *topic_name = Call<const char *>(mgp_message_topic_name, self->message);
   auto *py_topic_name = PyUnicode_FromString(topic_name);
   if (!py_topic_name) {
     PyErr_SetString(PyExc_RuntimeError, "Unable to get raw bytes from payload");
@@ -449,8 +450,8 @@ PyObject *PyMessageGetTopicName(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
 PyObject *PyMessageGetKey(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->message);
   MG_ASSERT(self->memory);
-  auto key_size = mgp_message_key_size(self->message);
-  const auto *key = mgp_message_key(self->message);
+  auto key_size = Call<size_t>(mgp_message_key_size, self->message);
+  const auto *key = Call<const char *>(mgp_message_key, self->message);
   auto *raw_bytes = PyByteArray_FromStringAndSize(key, key_size);
   if (!raw_bytes) {
     PyErr_SetString(PyExc_RuntimeError, "Unable to get raw bytes from payload");
@@ -462,7 +463,7 @@ PyObject *PyMessageGetKey(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
 PyObject *PyMessageGetTimestamp(PyMessage *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->message);
   MG_ASSERT(self->memory);
-  auto timestamp = mgp_message_timestamp(self->message);
+  auto timestamp = Call<int64_t>(mgp_message_timestamp, self->message);
   auto *py_int = PyLong_FromUnsignedLong(timestamp);
   if (!py_int) {
     PyErr_SetString(PyExc_IndexError, "Unable to get timestamp.");
@@ -529,7 +530,7 @@ PyObject *PyMessagesGetMessageAt(PyMessages *self, PyObject *args) {
   int64_t id = 0;
   if (!PyArg_ParseTuple(args, "l", &id)) return nullptr;
   if (id < 0) return nullptr;
-  const auto *message = mgp_messages_at(self->messages, id);
+  const auto *message = Call<const mgp_message *>(mgp_messages_at, self->messages, id);
   // NOLINTNEXTLINE
   auto *py_message = PyObject_New(PyMessage, &PyMessageType);
   if (!py_message) {
@@ -586,11 +587,11 @@ PyObject *MakePyMessages(const mgp_messages *msgs, mgp_memory *memory) {
 py::Object MgpListToPyTuple(const mgp_list *list, PyGraph *py_graph) {
   MG_ASSERT(list);
   MG_ASSERT(py_graph);
-  const size_t len = mgp_list_size(list);
+  const auto len = Call<size_t>(mgp_list_size, list);
   py::Object py_tuple(PyTuple_New(len));
   if (!py_tuple) return nullptr;
   for (size_t i = 0; i < len; ++i) {
-    auto elem = MgpValueToPyObject(*mgp_list_at(list, i), py_graph);
+    auto elem = MgpValueToPyObject(*Call<const mgp_value *>(mgp_list_at, list, i), py_graph);
     if (!elem) return nullptr;
     // Explicitly convert `py_tuple`, which is `py::Object`, via static_cast.
     // Then the macro will cast it to `PyTuple *`.
@@ -629,7 +630,7 @@ std::optional<py::ExceptionInfo> AddRecordFromPython(mgp_result *result, py::Obj
   }
   py::Object items(PyDict_Items(fields.Ptr()));
   if (!items) return py::FetchError();
-  auto *record = mgp_result_new_record(result);
+  auto *record = Call<mgp_result_record *>(mgp_result_new_record, result);
   if (!record) {
     PyErr_NoMemory();
     return py::FetchError();
@@ -662,7 +663,7 @@ std::optional<py::ExceptionInfo> AddRecordFromPython(mgp_result *result, py::Obj
       return py::FetchError();
     }
     MG_ASSERT(field_val);
-    if (!mgp_result_record_insert(record, field_name, field_val)) {
+    if (mgp_result_record_insert(record, field_name, field_val) != MGP_ERROR_NO_ERROR) {
       std::stringstream ss;
       ss << "Unable to insert field '" << py::Object::FromBorrow(key) << "' with value: '"
          << py::Object::FromBorrow(val) << "'; did you set the correct field type?";
@@ -764,7 +765,7 @@ void CallPythonProcedure(const py::Object &py_cb, const mgp_list *args, const mg
   }
 
   if (maybe_msg) {
-    mgp_result_set_error_msg(result, maybe_msg->c_str());
+    static_cast<void>(mgp_result_set_error_msg(result, maybe_msg->c_str()));
   }
 }
 
@@ -845,7 +846,7 @@ void CallPythonTransformation(const py::Object &py_cb, const mgp_messages *msgs,
   }
 
   if (maybe_msg) {
-    mgp_result_set_error_msg(result, maybe_msg->c_str());
+    static_cast<void>(mgp_result_set_error_msg(result, maybe_msg->c_str()));
   }
 }
 }  // namespace
@@ -945,7 +946,7 @@ PyObject *PyMgpModuleTypeNullable(PyObject *mod, PyObject *obj) {
     return nullptr;
   }
   auto *py_type = reinterpret_cast<PyCypherType *>(obj);
-  return MakePyCypherType(mgp_type_nullable(py_type->type));
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_nullable, py_type->type));
 }
 
 PyObject *PyMgpModuleTypeList(PyObject *mod, PyObject *obj) {
@@ -954,36 +955,48 @@ PyObject *PyMgpModuleTypeList(PyObject *mod, PyObject *obj) {
     return nullptr;
   }
   auto *py_type = reinterpret_cast<PyCypherType *>(obj);
-  return MakePyCypherType(mgp_type_list(py_type->type));
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_list, py_type->type));
 }
 
-PyObject *PyMgpModuleTypeAny(PyObject *mod, PyObject *Py_UNUSED(ignored)) { return MakePyCypherType(mgp_type_any()); }
-
-PyObject *PyMgpModuleTypeBool(PyObject *mod, PyObject *Py_UNUSED(ignored)) { return MakePyCypherType(mgp_type_bool()); }
-
-PyObject *PyMgpModuleTypeString(PyObject *mod, PyObject *Py_UNUSED(ignored)) {
-  return MakePyCypherType(mgp_type_string());
+PyObject *PyMgpModuleTypeAny(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_any));
 }
 
-PyObject *PyMgpModuleTypeInt(PyObject *mod, PyObject *Py_UNUSED(ignored)) { return MakePyCypherType(mgp_type_int()); }
-
-PyObject *PyMgpModuleTypeFloat(PyObject *mod, PyObject *Py_UNUSED(ignored)) {
-  return MakePyCypherType(mgp_type_float());
+PyObject *PyMgpModuleTypeBool(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_bool));
 }
 
-PyObject *PyMgpModuleTypeNumber(PyObject *mod, PyObject *Py_UNUSED(ignored)) {
-  return MakePyCypherType(mgp_type_number());
+PyObject *PyMgpModuleTypeString(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_string));
 }
 
-PyObject *PyMgpModuleTypeMap(PyObject *mod, PyObject *Py_UNUSED(ignored)) { return MakePyCypherType(mgp_type_map()); }
-
-PyObject *PyMgpModuleTypeNode(PyObject *mod, PyObject *Py_UNUSED(ignored)) { return MakePyCypherType(mgp_type_node()); }
-
-PyObject *PyMgpModuleTypeRelationship(PyObject *mod, PyObject *Py_UNUSED(ignored)) {
-  return MakePyCypherType(mgp_type_relationship());
+PyObject *PyMgpModuleTypeInt(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_int));
 }
 
-PyObject *PyMgpModuleTypePath(PyObject *mod, PyObject *Py_UNUSED(ignored)) { return MakePyCypherType(mgp_type_path()); }
+PyObject *PyMgpModuleTypeFloat(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_float));
+}
+
+PyObject *PyMgpModuleTypeNumber(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_number));
+}
+
+PyObject *PyMgpModuleTypeMap(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_map));
+}
+
+PyObject *PyMgpModuleTypeNode(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_node));
+}
+
+PyObject *PyMgpModuleTypeRelationship(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_relationship));
+}
+
+PyObject *PyMgpModuleTypePath(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
+  return MakePyCypherType(Call<const mgp_type *>(mgp_type_path));
+}
 
 static PyMethodDef PyMgpModuleMethods[] = {
     {"type_nullable", PyMgpModuleTypeNullable, METH_O,
@@ -1038,7 +1051,7 @@ PyObject *PyPropertiesIteratorGet(PyPropertiesIterator *self, PyObject *Py_UNUSE
   MG_ASSERT(self->it);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *property = mgp_properties_iterator_get(self->it);
+  const auto *property = Call<const mgp_property *>(mgp_properties_iterator_get, self->it);
   if (!property) Py_RETURN_NONE;
   py::Object py_name(PyUnicode_FromString(property->name));
   if (!py_name) return nullptr;
@@ -1051,7 +1064,7 @@ PyObject *PyPropertiesIteratorNext(PyPropertiesIterator *self, PyObject *Py_UNUS
   MG_ASSERT(self->it);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *property = mgp_properties_iterator_next(self->it);
+  const auto *property = Call<const mgp_property *>(mgp_properties_iterator_next, self->it);
   if (!property) Py_RETURN_NONE;
   py::Object py_name(PyUnicode_FromString(property->name));
   if (!py_name) return nullptr;
@@ -1093,7 +1106,7 @@ PyObject *PyEdgeGetTypeName(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->edge);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  return PyUnicode_FromString(mgp_edge_get_type(self->edge).name);
+  return PyUnicode_FromString(Call<mgp_edge_type>(mgp_edge_get_type, self->edge).name);
 }
 
 PyObject *PyEdgeFromVertex(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
@@ -1101,7 +1114,7 @@ PyObject *PyEdgeFromVertex(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->edge);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *vertex = mgp_edge_get_from(self->edge);
+  const auto *vertex = Call<const mgp_vertex *>(mgp_edge_get_from, self->edge);
   MG_ASSERT(vertex);
   return MakePyVertex(*vertex, self->py_graph);
 }
@@ -1111,7 +1124,7 @@ PyObject *PyEdgeToVertex(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->edge);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  const auto *vertex = mgp_edge_get_to(self->edge);
+  const auto *vertex = Call<const mgp_vertex *>(mgp_edge_get_to, self->edge);
   MG_ASSERT(vertex);
   return MakePyVertex(*vertex, self->py_graph);
 }
@@ -1136,7 +1149,7 @@ PyObject *PyEdgeGetId(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->edge);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  return PyLong_FromLongLong(mgp_edge_get_id(self->edge).as_int);
+  return PyLong_FromLongLong(Call<mgp_edge_id>(mgp_edge_get_id, self->edge).as_int);
 }
 
 PyObject *PyEdgeIterProperties(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
@@ -1144,10 +1157,14 @@ PyObject *PyEdgeIterProperties(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->edge);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  auto *properties_it = mgp_edge_iter_properties(self->edge, self->py_graph->memory);
-  if (!properties_it) {
+  mgp_properties_iterator *properties_it{nullptr};
+  if (const auto err = mgp_edge_iter_properties(self->edge, self->py_graph->memory, &properties_it);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_properties_iterator.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting mgp_properties_iterator.");
+    return nullptr;
   }
   auto *py_properties_it = PyObject_New(PyPropertiesIterator, &PyPropertiesIteratorType);
   if (!py_properties_it) {
@@ -1167,10 +1184,14 @@ PyObject *PyEdgeGetProperty(PyEdge *self, PyObject *args) {
   MG_ASSERT(self->py_graph->graph);
   const char *prop_name = nullptr;
   if (!PyArg_ParseTuple(args, "s", &prop_name)) return nullptr;
-  auto *prop_value = mgp_edge_get_property(self->edge, prop_name, self->py_graph->memory);
-  if (!prop_value) {
+  mgp_value *prop_value{nullptr};
+  if (const auto err = mgp_edge_get_property(self->edge, prop_name, self->py_graph->memory, &prop_value);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_value for edge property value.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting mgp_edge property.");
+    return nullptr;
   }
   auto py_prop_value = MgpValueToPyObject(*prop_value, self->py_graph);
   mgp_value_destroy(prop_value);
@@ -1214,10 +1235,13 @@ static PyTypeObject PyEdgeType = {
 PyObject *MakePyEdge(const mgp_edge &edge, PyGraph *py_graph) {
   MG_ASSERT(py_graph);
   MG_ASSERT(py_graph->graph && py_graph->memory);
-  auto *edge_copy = mgp_edge_copy(&edge, py_graph->memory);
-  if (!edge_copy) {
+  mgp_edge *edge_copy{nullptr};
+  if (const auto err = mgp_edge_copy(&edge, py_graph->memory, &edge_copy); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_edge.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during creating mgp_edge");
+    return nullptr;
   }
   auto *py_edge = PyObject_New(PyEdge, &PyEdgeType);
   if (!py_edge) {
@@ -1243,7 +1267,12 @@ PyObject *PyEdgeRichCompare(PyObject *self, PyObject *other, int op) {
   MG_ASSERT(e1->edge);
   MG_ASSERT(e2->edge);
 
-  return PyBool_FromLong(mgp_edge_equal(e1->edge, e2->edge));
+  int equals{0};
+  if (const auto err = mgp_edge_equal(e1->edge, e2->edge, &equals); err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during comparing edges");
+    return nullptr;
+  }
+  return PyBool_FromLong(equals);
 }
 
 // clang-format off
@@ -1274,7 +1303,12 @@ PyObject *PyVertexGetId(PyVertex *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->vertex);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  return PyLong_FromLongLong(mgp_vertex_get_id(self->vertex).as_int);
+  mgp_vertex_id id{};
+  if (const auto err = mgp_vertex_get_id(self->vertex, &id); err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting id of mgp_vertex");
+    return nullptr;
+  }
+  return PyLong_FromLongLong(id.as_int);
 }
 
 PyObject *PyVertexLabelsCount(PyVertex *self, PyObject *Py_UNUSED(ignored)) {
@@ -1282,7 +1316,12 @@ PyObject *PyVertexLabelsCount(PyVertex *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->vertex);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  return PyLong_FromSize_t(mgp_vertex_labels_count(self->vertex));
+  size_t label_count{0};
+  if (const auto err = mgp_vertex_labels_count(self->vertex, &label_count); err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting label count of mgp_vertex");
+    return nullptr;
+  }
+  return PyLong_FromSize_t(label_count);
 }
 
 PyObject *PyVertexLabelAt(PyVertex *self, PyObject *args) {
@@ -1292,8 +1331,14 @@ PyObject *PyVertexLabelAt(PyVertex *self, PyObject *args) {
   MG_ASSERT(self->py_graph->graph);
   static_assert(std::numeric_limits<Py_ssize_t>::max() <= std::numeric_limits<size_t>::max());
   Py_ssize_t id;
-  if (!PyArg_ParseTuple(args, "n", &id)) return nullptr;
-  auto label = mgp_vertex_label_at(self->vertex, id);
+  if (!PyArg_ParseTuple(args, "n", &id)) {
+    return nullptr;
+  }
+  mgp_label label{nullptr};
+  if (const auto err = mgp_vertex_label_at(self->vertex, id, &label); err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting label of mgp_vertex");
+    return nullptr;
+  }
   if (label.name == nullptr || id < 0) {
     PyErr_SetString(PyExc_IndexError, "Unable to find the label with given ID.");
     return nullptr;
@@ -1306,10 +1351,14 @@ PyObject *PyVertexIterInEdges(PyVertex *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->vertex);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  auto *edges_it = mgp_vertex_iter_in_edges(self->vertex, self->py_graph->memory);
-  if (!edges_it) {
+  mgp_edges_iterator *edges_it{nullptr};
+  if (const auto err = mgp_vertex_iter_in_edges(self->vertex, self->py_graph->memory, &edges_it);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_edges_iterator for in edges.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting mgp_edges_iterator for in edges");
+    return nullptr;
   }
   auto *py_edges_it = PyObject_New(PyEdgesIterator, &PyEdgesIteratorType);
   if (!py_edges_it) {
@@ -1327,10 +1376,14 @@ PyObject *PyVertexIterOutEdges(PyVertex *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->vertex);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  auto *edges_it = mgp_vertex_iter_out_edges(self->vertex, self->py_graph->memory);
-  if (!edges_it) {
+  mgp_edges_iterator *edges_it{nullptr};
+  if (const auto err = mgp_vertex_iter_out_edges(self->vertex, self->py_graph->memory, &edges_it);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_edges_iterator for out edges.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting mgp_edges_iterator for out edges");
+    return nullptr;
   }
   auto *py_edges_it = PyObject_New(PyEdgesIterator, &PyEdgesIteratorType);
   if (!py_edges_it) {
@@ -1348,10 +1401,14 @@ PyObject *PyVertexIterProperties(PyVertex *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->vertex);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  auto *properties_it = mgp_vertex_iter_properties(self->vertex, self->py_graph->memory);
-  if (!properties_it) {
+  mgp_properties_iterator *properties_it{nullptr};
+  if (const auto err = mgp_vertex_iter_properties(self->vertex, self->py_graph->memory, &properties_it);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_properties_iterator.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting mgp_properties_iterator.");
+    return nullptr;
   }
   auto *py_properties_it = PyObject_New(PyPropertiesIterator, &PyPropertiesIteratorType);
   if (!py_properties_it) {
@@ -1370,11 +1427,17 @@ PyObject *PyVertexGetProperty(PyVertex *self, PyObject *args) {
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
   const char *prop_name = nullptr;
-  if (!PyArg_ParseTuple(args, "s", &prop_name)) return nullptr;
-  auto *prop_value = mgp_vertex_get_property(self->vertex, prop_name, self->py_graph->memory);
-  if (!prop_value) {
+  if (!PyArg_ParseTuple(args, "s", &prop_name)) {
+    return nullptr;
+  }
+  mgp_value *prop_value{nullptr};
+  if (const auto err = mgp_vertex_get_property(self->vertex, prop_name, self->py_graph->memory, &prop_value);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_value for vertex property value.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting mgp_vertex property.");
+    return nullptr;
   }
   auto py_prop_value = MgpValueToPyObject(*prop_value, self->py_graph);
   mgp_value_destroy(prop_value);
@@ -1432,10 +1495,14 @@ PyObject *MakePyVertex(mgp_vertex *vertex, PyGraph *py_graph) {
 PyObject *MakePyVertex(const mgp_vertex &vertex, PyGraph *py_graph) {
   MG_ASSERT(py_graph);
   MG_ASSERT(py_graph->graph && py_graph->memory);
-  auto *vertex_copy = mgp_vertex_copy(&vertex, py_graph->memory);
-  if (!vertex_copy) {
+
+  mgp_vertex *vertex_copy{nullptr};
+  if (const auto err = mgp_vertex_copy(&vertex, py_graph->memory, &vertex_copy); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_vertex.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during creating mgp_vertex");
+    return nullptr;
   }
   auto *py_vertex = MakePyVertex(vertex_copy, py_graph);
   if (!py_vertex) mgp_vertex_destroy(vertex_copy);
@@ -1455,7 +1522,12 @@ PyObject *PyVertexRichCompare(PyObject *self, PyObject *other, int op) {
   MG_ASSERT(v1->vertex);
   MG_ASSERT(v2->vertex);
 
-  return PyBool_FromLong(mgp_vertex_equal(v1->vertex, v2->vertex));
+  int equals{0};
+  if (const auto err = mgp_vertex_equal(v1->vertex, v2->vertex, &equals); err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during comparing vertices");
+    return nullptr;
+  }
+  return PyBool_FromLong(equals);
 }
 
 // clang-format off
@@ -1492,14 +1564,10 @@ PyObject *PyPathExpand(PyPath *self, PyObject *edge) {
     return nullptr;
   }
   auto *py_edge = reinterpret_cast<PyEdge *>(edge);
-  const auto *to = mgp_edge_get_to(py_edge->edge);
-  const auto *from = mgp_edge_get_from(py_edge->edge);
-  const auto *last_vertex = mgp_path_vertex_at(self->path, mgp_path_size(self->path));
-  if (!mgp_vertex_equal(last_vertex, to) && !mgp_vertex_equal(last_vertex, from)) {
+  if (const auto err = mgp_path_expand(self->path, py_edge->edge); err == MGP_ERROR_LOGIC_ERROR) {
     PyErr_SetString(PyExc_ValueError, "Edge is not a continuation of the path.");
     return nullptr;
-  }
-  if (!mgp_path_expand(self->path, py_edge->edge)) {
+  } else if (err != MGP_ERROR_NO_ERROR) {
     PyErr_SetString(PyExc_MemoryError, "Unable to expand mgp_path.");
     return nullptr;
   }
@@ -1510,7 +1578,7 @@ PyObject *PyPathSize(PyPath *self, PyObject *Py_UNUSED(ignored)) {
   MG_ASSERT(self->path);
   MG_ASSERT(self->py_graph);
   MG_ASSERT(self->py_graph->graph);
-  return PyLong_FromSize_t(mgp_path_size(self->path));
+  return PyLong_FromSize_t(Call<size_t>(mgp_path_size, self->path));
 }
 
 PyObject *PyPathVertexAt(PyPath *self, PyObject *args) {
@@ -1519,11 +1587,16 @@ PyObject *PyPathVertexAt(PyPath *self, PyObject *args) {
   MG_ASSERT(self->py_graph->graph);
   static_assert(std::numeric_limits<Py_ssize_t>::max() <= std::numeric_limits<size_t>::max());
   Py_ssize_t i;
-  if (!PyArg_ParseTuple(args, "n", &i)) return nullptr;
-  const auto *vertex = mgp_path_vertex_at(self->path, i);
-  if (!vertex) {
+  if (!PyArg_ParseTuple(args, "n", &i)) {
+    return nullptr;
+  }
+  const mgp_vertex *vertex{nullptr};
+  if (const auto err = mgp_path_vertex_at(self->path, i, &vertex); err == MGP_ERROR_OUT_OF_RANGE) {
     PyErr_SetString(PyExc_IndexError, "Index is out of range.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting vertex from path.");
+    return nullptr;
   }
   return MakePyVertex(*vertex, self->py_graph);
 }
@@ -1534,11 +1607,16 @@ PyObject *PyPathEdgeAt(PyPath *self, PyObject *args) {
   MG_ASSERT(self->py_graph->graph);
   static_assert(std::numeric_limits<Py_ssize_t>::max() <= std::numeric_limits<size_t>::max());
   Py_ssize_t i;
-  if (!PyArg_ParseTuple(args, "n", &i)) return nullptr;
-  const auto *edge = mgp_path_edge_at(self->path, i);
-  if (!edge) {
+  if (!PyArg_ParseTuple(args, "n", &i)) {
+    return nullptr;
+  }
+  const mgp_edge *edge{nullptr};
+  if (const auto err = mgp_path_edge_at(self->path, i, &edge); err == MGP_ERROR_OUT_OF_RANGE) {
     PyErr_SetString(PyExc_IndexError, "Index is out of range.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during getting edge from path.");
+    return nullptr;
   }
   return MakePyEdge(*edge, self->py_graph);
 }
@@ -1586,10 +1664,13 @@ PyObject *MakePyPath(mgp_path *path, PyGraph *py_graph) {
 PyObject *MakePyPath(const mgp_path &path, PyGraph *py_graph) {
   MG_ASSERT(py_graph);
   MG_ASSERT(py_graph->graph && py_graph->memory);
-  auto *path_copy = mgp_path_copy(&path, py_graph->memory);
-  if (!path_copy) {
+  mgp_path *path_copy{nullptr};
+  if (const auto err = mgp_path_copy(&path, py_graph->memory, &path_copy); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_path.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during copying a path");
+    return nullptr;
   }
   auto *py_path = MakePyPath(path_copy, py_graph);
   if (!py_path) mgp_path_destroy(path_copy);
@@ -1606,10 +1687,14 @@ PyObject *PyPathMakeWithStart(PyTypeObject *type, PyObject *vertex) {
     return nullptr;
   }
   auto *py_vertex = reinterpret_cast<PyVertex *>(vertex);
-  auto *path = mgp_path_make_with_start(py_vertex->vertex, py_vertex->py_graph->memory);
-  if (!path) {
+  mgp_path *path{nullptr};
+  if (const auto err = mgp_path_make_with_start(py_vertex->vertex, py_vertex->py_graph->memory, &path);
+      err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
     PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_path.");
     return nullptr;
+  } else if (err != MGP_ERROR_NO_ERROR) {
+    PyErr_SetString(PyExc_RuntimeError, "Unexpected error during creating a path");
+    return nullptr;
   }
   auto *py_path = MakePyPath(path, py_vertex->py_graph);
   if (!py_path) mgp_path_destroy(path);
@@ -1698,27 +1783,31 @@ py::Object MgpValueToPyObject(const mgp_value &value, PyObject *py_graph) {
 }
 
 py::Object MgpValueToPyObject(const mgp_value &value, PyGraph *py_graph) {
-  switch (mgp_value_get_type(&value)) {
+  switch (Call<mgp_value_type>(mgp_value_get_type, &value)) {
     case MGP_VALUE_TYPE_NULL:
       Py_INCREF(Py_None);
       return py::Object(Py_None);
     case MGP_VALUE_TYPE_BOOL:
-      return py::Object(PyBool_FromLong(mgp_value_get_bool(&value)));
+      return py::Object(PyBool_FromLong(CallBool(mgp_value_get_bool, &value)));
     case MGP_VALUE_TYPE_INT:
-      return py::Object(PyLong_FromLongLong(mgp_value_get_int(&value)));
+      return py::Object(PyLong_FromLongLong(Call<int64_t>(mgp_value_get_int, &value)));
     case MGP_VALUE_TYPE_DOUBLE:
-      return py::Object(PyFloat_FromDouble(mgp_value_get_double(&value)));
+      return py::Object(PyFloat_FromDouble(Call<double>(mgp_value_get_double, &value)));
     case MGP_VALUE_TYPE_STRING:
-      return py::Object(PyUnicode_FromString(mgp_value_get_string(&value)));
+      return py::Object(PyUnicode_FromString(Call<const char *>(mgp_value_get_string, &value)));
     case MGP_VALUE_TYPE_LIST:
-      return MgpListToPyTuple(mgp_value_get_list(&value), py_graph);
+      return MgpListToPyTuple(Call<const mgp_list *>(mgp_value_get_list, &value), py_graph);
     case MGP_VALUE_TYPE_MAP: {
-      const auto *map = mgp_value_get_map(&value);
+      const auto *map = Call<const mgp_map *>(mgp_value_get_map, &value);
       py::Object py_dict(PyDict_New());
-      if (!py_dict) return nullptr;
+      if (!py_dict) {
+        return nullptr;
+      }
       for (const auto &[key, val] : map->items) {
         auto py_val = MgpValueToPyObject(val, py_graph);
-        if (!py_val) return nullptr;
+        if (!py_val) {
+          return nullptr;
+        }
         // Unlike PyList_SET_ITEM, PyDict_SetItem does not steal the value.
         if (PyDict_SetItemString(py_dict.Ptr(), key.c_str(), py_val.Ptr()) != 0) return nullptr;
       }
@@ -1727,21 +1816,21 @@ py::Object MgpValueToPyObject(const mgp_value &value, PyGraph *py_graph) {
     case MGP_VALUE_TYPE_VERTEX: {
       py::Object py_mgp(PyImport_ImportModule("mgp"));
       if (!py_mgp) return nullptr;
-      const auto *v = mgp_value_get_vertex(&value);
+      const auto *v = Call<const mgp_vertex *>(mgp_value_get_vertex, &value);
       py::Object py_vertex(reinterpret_cast<PyObject *>(MakePyVertex(*v, py_graph)));
       return py_mgp.CallMethod("Vertex", py_vertex);
     }
     case MGP_VALUE_TYPE_EDGE: {
       py::Object py_mgp(PyImport_ImportModule("mgp"));
       if (!py_mgp) return nullptr;
-      const auto *e = mgp_value_get_edge(&value);
+      const auto *e = Call<const mgp_edge *>(mgp_value_get_edge, &value);
       py::Object py_edge(reinterpret_cast<PyObject *>(MakePyEdge(*e, py_graph)));
       return py_mgp.CallMethod("Edge", py_edge);
     }
     case MGP_VALUE_TYPE_PATH: {
       py::Object py_mgp(PyImport_ImportModule("mgp"));
       if (!py_mgp) return nullptr;
-      const auto *p = mgp_value_get_path(&value);
+      const auto *p = Call<const mgp_path *>(mgp_value_get_path, &value);
       py::Object py_path(reinterpret_cast<PyObject *>(MakePyPath(*p, py_graph)));
       return py_mgp.CallMethod("Path", py_path);
     }
@@ -1751,29 +1840,33 @@ py::Object MgpValueToPyObject(const mgp_value &value, PyGraph *py_graph) {
 mgp_value *PyObjectToMgpValue(PyObject *o, mgp_memory *memory) {
   auto py_seq_to_list = [memory](PyObject *seq, Py_ssize_t len, const auto &py_seq_get_item) {
     static_assert(std::numeric_limits<Py_ssize_t>::max() <= std::numeric_limits<size_t>::max());
-    mgp_list *list = mgp_list_make_empty(len, memory);
-    if (!list) throw std::bad_alloc();
+    MgpUniquePtr<mgp_list> list{nullptr, &mgp_list_destroy};
+    if (const auto err = CreateMgpObject<mgp_list>(list, mgp_list_make_empty, len, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during making mgp_list"};
+    }
     for (Py_ssize_t i = 0; i < len; ++i) {
       PyObject *e = py_seq_get_item(seq, i);
       mgp_value *v{nullptr};
-      try {
-        v = PyObjectToMgpValue(e, memory);
-      } catch (...) {
-        mgp_list_destroy(list);
-        throw;
-      }
-      if (!mgp_list_append(list, v)) {
-        mgp_value_destroy(v);
-        mgp_list_destroy(list);
-        throw std::bad_alloc();
-      }
+      v = PyObjectToMgpValue(e, memory);
+      const auto err = mgp_list_append(list.get(), v);
       mgp_value_destroy(v);
+      if (err != MGP_ERROR_NO_ERROR) {
+        if (err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+          throw std::bad_alloc{};
+        }
+        throw std::runtime_error{"Unexpected error during appending to mgp_list"};
+      }
     }
-    auto *v = mgp_value_make_list(list);
-    if (!v) {
-      mgp_list_destroy(list);
-      throw std::bad_alloc();
+    mgp_value *v{nullptr};
+    if (const auto err = mgp_value_make_list(list.get(), &v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during making mgp_value"};
     }
+    static_cast<void>(list.release());
     return v;
   };
 
@@ -1802,31 +1895,37 @@ mgp_value *PyObjectToMgpValue(PyObject *o, mgp_memory *memory) {
   };
 
   mgp_value *mgp_v{nullptr};
+  mgp_error last_error{MGP_ERROR_NO_ERROR};
 
   if (o == Py_None) {
-    mgp_v = mgp_value_make_null(memory);
+    last_error = mgp_value_make_null(memory, &mgp_v);
   } else if (PyBool_Check(o)) {
-    mgp_v = mgp_value_make_bool(static_cast<int>(o == Py_True), memory);
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) Py_True is defined with C-style cast
+    last_error = mgp_value_make_bool(static_cast<int>(o == Py_True), memory, &mgp_v);
   } else if (PyLong_Check(o)) {
     int64_t value = PyLong_AsLong(o);
     if (PyErr_Occurred()) {
       PyErr_Clear();
       throw std::overflow_error("Python integer is out of range");
     }
-    mgp_v = mgp_value_make_int(value, memory);
+    last_error = mgp_value_make_int(value, memory, &mgp_v);
   } else if (PyFloat_Check(o)) {
-    mgp_v = mgp_value_make_double(PyFloat_AsDouble(o), memory);
-  } else if (PyUnicode_Check(o)) {
-    mgp_v = mgp_value_make_string(PyUnicode_AsUTF8(o), memory);
+    last_error = mgp_value_make_double(PyFloat_AsDouble(o), memory, &mgp_v);
+  } else if (PyUnicode_Check(o)) {  // NOLINT(hicpp-signed-bitwise)
+    last_error = mgp_value_make_string(PyUnicode_AsUTF8(o), memory, &mgp_v);
   } else if (PyList_Check(o)) {
     mgp_v = py_seq_to_list(o, PyList_Size(o), [](auto *list, const auto i) { return PyList_GET_ITEM(list, i); });
   } else if (PyTuple_Check(o)) {
     mgp_v = py_seq_to_list(o, PyTuple_Size(o), [](auto *tuple, const auto i) { return PyTuple_GET_ITEM(tuple, i); });
-  } else if (PyDict_Check(o)) {
-    mgp_map *map = mgp_map_make_empty(memory);
+  } else if (PyDict_Check(o)) {  // NOLINT(hicpp-signed-bitwise)
+    MgpUniquePtr<mgp_map> map{nullptr, mgp_map_destroy};
+    const auto map_err = CreateMgpObject(map, mgp_map_make_empty, memory);
 
-    if (!map) {
-      throw std::bad_alloc();
+    if (map_err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    }
+    if (map_err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during creating mgp_map"};
     }
 
     PyObject *key{nullptr};
@@ -1834,73 +1933,79 @@ mgp_value *PyObjectToMgpValue(PyObject *o, mgp_memory *memory) {
     Py_ssize_t pos{0};
     while (PyDict_Next(o, &pos, &key, &value)) {
       if (!PyUnicode_Check(key)) {
-        mgp_map_destroy(map);
         throw std::invalid_argument("Dictionary keys must be strings");
       }
 
       const char *k = PyUnicode_AsUTF8(key);
-      mgp_value *v{nullptr};
 
       if (!k) {
         PyErr_Clear();
-        mgp_map_destroy(map);
-        throw std::bad_alloc();
+        throw std::bad_alloc{};
       }
 
-      try {
-        v = PyObjectToMgpValue(value, memory);
-      } catch (...) {
-        mgp_map_destroy(map);
-        throw;
-      }
+      MgpUniquePtr<mgp_value> v{PyObjectToMgpValue(value, memory), mgp_value_destroy};
 
-      if (!mgp_map_insert(map, k, v)) {
-        mgp_value_destroy(v);
-        mgp_map_destroy(map);
-        throw std::bad_alloc();
+      if (const auto err = mgp_map_insert(map.get(), k, v.get()); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+        throw std::bad_alloc{};
+      } else if (err != MGP_ERROR_NO_ERROR) {
+        throw std::runtime_error{"Unexpected error during inserting an item to mgp_map"};
       }
-
-      mgp_value_destroy(v);
     }
 
-    mgp_v = mgp_value_make_map(map);
-    if (!mgp_v) {
-      mgp_map_destroy(map);
-      throw std::bad_alloc();
+    if (const auto err = mgp_value_make_map(map.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during creating mgp_value"};
     }
+    static_cast<void>(map.release());
   } else if (Py_TYPE(o) == &PyEdgeType) {
+    MgpUniquePtr<mgp_edge> e{nullptr, mgp_edge_destroy};
     // Copy the edge and pass the ownership to the created mgp_value.
-    auto *e = mgp_edge_copy(reinterpret_cast<PyEdge *>(o)->edge, memory);
-    if (!e) {
-      throw std::bad_alloc();
+
+    if (const auto err = CreateMgpObject(e, mgp_edge_copy, reinterpret_cast<PyEdge *>(o)->edge, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during copying mgp_edge"};
     }
-    mgp_v = mgp_value_make_edge(e);
-    if (!mgp_v) {
-      mgp_edge_destroy(e);
-      throw std::bad_alloc();
+    if (const auto err = mgp_value_make_edge(e.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during copying mgp_edge"};
     }
+    static_cast<void>(e.release());
   } else if (Py_TYPE(o) == &PyPathType) {
-    // Copy the path and pass the ownership to the created mgp_value.
-    auto *p = mgp_path_copy(reinterpret_cast<PyPath *>(o)->path, memory);
-    if (!p) {
-      throw std::bad_alloc();
+    MgpUniquePtr<mgp_path> p{nullptr, mgp_path_destroy};
+    // Copy the edge and pass the ownership to the created mgp_value.
+
+    if (const auto err = CreateMgpObject(p, mgp_path_copy, reinterpret_cast<PyPath *>(o)->path, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during copying mgp_path"};
     }
-    mgp_v = mgp_value_make_path(p);
-    if (!mgp_v) {
-      mgp_path_destroy(p);
-      throw std::bad_alloc();
+    if (const auto err = mgp_value_make_path(p.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during copying mgp_path"};
     }
+    static_cast<void>(p.release());
   } else if (Py_TYPE(o) == &PyVertexType) {
-    // Copy the vertex and pass the ownership to the created mgp_value.
-    auto *v = mgp_vertex_copy(reinterpret_cast<PyVertex *>(o)->vertex, memory);
-    if (!v) {
-      throw std::bad_alloc();
+    MgpUniquePtr<mgp_vertex> v{nullptr, mgp_vertex_destroy};
+    // Copy the edge and pass the ownership to the created mgp_value.
+
+    if (const auto err = CreateMgpObject(v, mgp_vertex_copy, reinterpret_cast<PyVertex *>(o)->vertex, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during copying mgp_vertex"};
     }
-    mgp_v = mgp_value_make_vertex(v);
-    if (!mgp_v) {
-      mgp_vertex_destroy(v);
-      throw std::bad_alloc();
+    if (const auto err = mgp_value_make_vertex(v.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error during copying mgp_vertex"};
     }
+    static_cast<void>(v.release());
   } else if (is_mgp_instance(o, "Edge")) {
     py::Object edge(PyObject_GetAttrString(o, "_edge"));
     if (!edge) {
@@ -1926,8 +2031,11 @@ mgp_value *PyObjectToMgpValue(PyObject *o, mgp_memory *memory) {
     throw std::invalid_argument("Unsupported PyObject conversion");
   }
 
-  if (!mgp_v) {
-    throw std::bad_alloc();
+  if (last_error == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+    throw std::bad_alloc{};
+  }
+  if (last_error != MGP_ERROR_NO_ERROR) {
+    throw std::runtime_error{"Unexpected error while creating mgp_value"};
   }
 
   return mgp_v;
diff --git a/src/storage/v2/edge_accessor.hpp b/src/storage/v2/edge_accessor.hpp
index 261a5a60e..b9445731e 100644
--- a/src/storage/v2/edge_accessor.hpp
+++ b/src/storage/v2/edge_accessor.hpp
@@ -57,7 +57,7 @@ class EdgeAccessor final {
   /// @throw std::bad_alloc
   Result<std::map<PropertyId, PropertyValue>> Properties(View view) const;
 
-  Gid Gid() const {
+  Gid Gid() const noexcept {
     if (config_.properties_on_edges) {
       return edge_.ptr->gid;
     } else {
@@ -67,10 +67,10 @@ class EdgeAccessor final {
 
   bool IsCycle() const { return from_vertex_ == to_vertex_; }
 
-  bool operator==(const EdgeAccessor &other) const {
+  bool operator==(const EdgeAccessor &other) const noexcept {
     return edge_ == other.edge_ && transaction_ == other.transaction_;
   }
-  bool operator!=(const EdgeAccessor &other) const { return !(*this == other); }
+  bool operator!=(const EdgeAccessor &other) const noexcept { return !(*this == other); }
 
  private:
   EdgeRef edge_;
diff --git a/src/storage/v2/edge_ref.hpp b/src/storage/v2/edge_ref.hpp
index 60e604284..b66907c6a 100644
--- a/src/storage/v2/edge_ref.hpp
+++ b/src/storage/v2/edge_ref.hpp
@@ -24,8 +24,8 @@ static_assert(std::is_standard_layout_v<Gid>, "The Gid must have a standard layo
 static_assert(std::is_standard_layout_v<Edge *>, "The Edge * must have a standard layout!");
 static_assert(std::is_standard_layout_v<EdgeRef>, "The EdgeRef must have a standard layout!");
 
-inline bool operator==(const EdgeRef &a, const EdgeRef &b) { return a.gid == b.gid; }
+inline bool operator==(const EdgeRef &a, const EdgeRef &b) noexcept { return a.gid == b.gid; }
 
-inline bool operator!=(const EdgeRef &a, const EdgeRef &b) { return a.gid != b.gid; }
+inline bool operator!=(const EdgeRef &a, const EdgeRef &b) noexcept { return a.gid != b.gid; }
 
 }  // namespace storage
diff --git a/src/storage/v2/vertex_accessor.hpp b/src/storage/v2/vertex_accessor.hpp
index a24f02a23..7b2787da1 100644
--- a/src/storage/v2/vertex_accessor.hpp
+++ b/src/storage/v2/vertex_accessor.hpp
@@ -83,12 +83,12 @@ class VertexAccessor final {
 
   Result<size_t> OutDegree(View view) const;
 
-  Gid Gid() const { return vertex_->gid; }
+  Gid Gid() const noexcept { return vertex_->gid; }
 
-  bool operator==(const VertexAccessor &other) const {
+  bool operator==(const VertexAccessor &other) const noexcept {
     return vertex_ == other.vertex_ && transaction_ == other.transaction_;
   }
-  bool operator!=(const VertexAccessor &other) const { return !(*this == other); }
+  bool operator!=(const VertexAccessor &other) const noexcept { return !(*this == other); }
 
  private:
   Vertex *vertex_;
diff --git a/src/utils/async_timer.cpp b/src/utils/async_timer.cpp
index afe90ce7f..8e8a0f081 100644
--- a/src/utils/async_timer.cpp
+++ b/src/utils/async_timer.cpp
@@ -168,7 +168,7 @@ AsyncTimer &AsyncTimer::operator=(AsyncTimer &&other) {
   return *this;
 };
 
-bool AsyncTimer::IsExpired() const {
+bool AsyncTimer::IsExpired() const noexcept {
   if (expiration_flag_ != nullptr) {
     return expiration_flag_->load(std::memory_order_relaxed);
   }
diff --git a/src/utils/async_timer.hpp b/src/utils/async_timer.hpp
index 4ac2ffc87..831db4e49 100644
--- a/src/utils/async_timer.hpp
+++ b/src/utils/async_timer.hpp
@@ -22,7 +22,7 @@ class AsyncTimer {
   AsyncTimer &operator=(const AsyncTimer &) = delete;
 
   // Returns false if the object isn't associated with any timer.
-  bool IsExpired() const;
+  bool IsExpired() const noexcept;
 
  private:
   void ReleaseResources();
diff --git a/src/utils/on_scope_exit.hpp b/src/utils/on_scope_exit.hpp
index 05897889b..257a22715 100644
--- a/src/utils/on_scope_exit.hpp
+++ b/src/utils/on_scope_exit.hpp
@@ -28,6 +28,10 @@ class OnScopeExit {
   explicit OnScopeExit(const std::function<void()> &function) : function_(function) {}
   ~OnScopeExit() { function_(); }
 
+  void Disable() {
+    function_ = [] {};
+  }
+
  private:
   std::function<void()> function_;
 };
diff --git a/tests/.clang-tidy b/tests/.clang-tidy
index d6676abd7..1a0f29304 100644
--- a/tests/.clang-tidy
+++ b/tests/.clang-tidy
@@ -39,7 +39,6 @@ Checks: '*,
          -hicpp-vararg,
          -llvm-header-guard,
          -misc-non-private-member-variables-in-classes,
-         -misc-unused-parameters,
          -modernize-avoid-c-arrays,
          -modernize-concat-nested-namespaces,
          -modernize-pass-by-value,
diff --git a/tests/e2e/memory/procedures/global_memory_limit.c b/tests/e2e/memory/procedures/global_memory_limit.c
index a2003cb59..8d4362afc 100644
--- a/tests/e2e/memory/procedures/global_memory_limit.c
+++ b/tests/e2e/memory/procedures/global_memory_limit.c
@@ -6,26 +6,32 @@ void set_error(struct mgp_result *result) { mgp_result_set_error_msg(result, "So
 
 static void procedure(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
                       struct mgp_memory *memory) {
-  struct mgp_result_record *record = mgp_result_new_record(result);
-  if (record == NULL) return set_error(result);
+  struct mgp_result_record *record = NULL;
+  const enum mgp_error new_record_err = mgp_result_new_record(result, &record);
+  if (new_record_err != MGP_ERROR_NO_ERROR) return set_error(result);
 
-  struct mgp_value *result_msg = mgp_value_make_string("mgp_init_module allocation works", memory);
-  if (result_msg == NULL) return set_error(result);
+  struct mgp_value *result_msg = NULL;
+  const enum mgp_error make_string_err = mgp_value_make_string("mgp_init_module allocation works", memory, &result_msg);
+  if (make_string_err != MGP_ERROR_NO_ERROR) return set_error(result);
 
-  int result_inserted = mgp_result_record_insert(record, "result", result_msg);
+  const enum mgp_error result_inserted = mgp_result_record_insert(record, "result", result_msg);
   mgp_value_destroy(result_msg);
-  if (!result_inserted) return set_error(result);
+  if (result_inserted != MGP_ERROR_NO_ERROR) return set_error(result);
 }
 
 int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
   const size_t one_gb = 1 << 30;
-  gVal = mgp_global_alloc(one_gb);
-  if (!gVal) return 1;
+  const enum mgp_error alloc_err = mgp_global_alloc(one_gb, &gVal);
+  if (alloc_err != MGP_ERROR_NO_ERROR) return 1;
 
-  struct mgp_proc *proc = mgp_module_add_read_procedure(module, "procedure", procedure);
-  if (!proc) return 1;
+  struct mgp_proc *proc = NULL;
+  const enum mgp_error proc_err = mgp_module_add_read_procedure(module, "procedure", procedure, &proc);
+  if (proc_err != MGP_ERROR_NO_ERROR) return 1;
 
-  if (!mgp_proc_add_result(proc, "result", mgp_type_string())) return 1;
+  const struct mgp_type *string_type = NULL;
+  const enum mgp_error string_type_err = mgp_type_string(&string_type);
+  if (string_type_err != MGP_ERROR_NO_ERROR) return 1;
+  if (mgp_proc_add_result(proc, "result", string_type) != MGP_ERROR_NO_ERROR) return 1;
 
   return 0;
 }
diff --git a/tests/e2e/memory/procedures/global_memory_limit_proc.c b/tests/e2e/memory/procedures/global_memory_limit_proc.c
index 519f11f05..95f7318f3 100644
--- a/tests/e2e/memory/procedures/global_memory_limit_proc.c
+++ b/tests/e2e/memory/procedures/global_memory_limit_proc.c
@@ -14,45 +14,57 @@ static void error(const struct mgp_list *args, const struct mgp_graph *graph, st
     gVal = NULL;
   }
   if (!gVal) {
-    gVal = mgp_global_alloc(one_gb);
-    if (!gVal) return set_out_of_memory_error(result);
+    const enum mgp_error err = mgp_global_alloc(one_gb, &gVal);
+    if (err == MGP_ERROR_UNABLE_TO_ALLOCATE) return set_out_of_memory_error(result);
+    if (err != MGP_ERROR_NO_ERROR) return set_error(result);
   }
-  struct mgp_result_record *record = mgp_result_new_record(result);
-  if (record == NULL) return set_error(result);
-  struct mgp_value *error_value = mgp_value_make_string("ERROR", memory);
-  if (error_value == NULL) return set_error(result);
-  int result_inserted = mgp_result_record_insert(record, "error_result", error_value);
+  struct mgp_result_record *record = NULL;
+  const enum mgp_error new_record_err = mgp_result_new_record(result, &record);
+  if (new_record_err != MGP_ERROR_NO_ERROR) return set_error(result);
+  struct mgp_value *error_value = NULL;
+  const enum mgp_error make_string_err = mgp_value_make_string("ERROR", memory, &error_value);
+  if (make_string_err != MGP_ERROR_NO_ERROR) return set_error(result);
+  const enum mgp_error result_inserted = mgp_result_record_insert(record, "error_result", error_value);
   mgp_value_destroy(error_value);
-  if (!result_inserted) return set_error(result);
+  if (result_inserted != MGP_ERROR_NO_ERROR) return set_error(result);
 }
 
 static void success(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
                     struct mgp_memory *memory) {
   const size_t bytes = 1024;
   if (!gVal) {
-    gVal = mgp_global_alloc(bytes);
-    if (!gVal) set_out_of_memory_error(result);
+    const enum mgp_error err = mgp_global_alloc(bytes, &gVal);
+    if (err == MGP_ERROR_UNABLE_TO_ALLOCATE) return set_out_of_memory_error(result);
+    if (err != MGP_ERROR_NO_ERROR) return set_error(result);
   }
 
-  struct mgp_result_record *record = mgp_result_new_record(result);
-  if (record == NULL) return set_error(result);
-  struct mgp_value *success_value = mgp_value_make_string("sucess", memory);
-  if (success_value == NULL) return set_error(result);
-  int result_inserted = mgp_result_record_insert(record, "success_result", success_value);
+  struct mgp_result_record *record = NULL;
+  const enum mgp_error new_record_err = mgp_result_new_record(result, &record);
+  if (new_record_err != MGP_ERROR_NO_ERROR) return set_error(result);
+
+  struct mgp_value *success_value = NULL;
+  const enum mgp_error make_string_err = mgp_value_make_string("sucess", memory, &success_value);
+  if (make_string_err != MGP_ERROR_NO_ERROR) return set_error(result);
+  const enum mgp_error result_inserted = mgp_result_record_insert(record, "success_result", success_value);
   mgp_value_destroy(success_value);
-  if (!result_inserted) return set_error(result);
+  if (result_inserted != MGP_ERROR_NO_ERROR) return set_error(result);
 }
 
 int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
-  struct mgp_proc *error_proc = mgp_module_add_read_procedure(module, "error", error);
-  if (!error_proc) return 1;
+  struct mgp_proc *error_proc = NULL;
+  const enum mgp_error error_proc_err = mgp_module_add_read_procedure(module, "error", error, &error_proc);
+  if (error_proc_err != MGP_ERROR_NO_ERROR) return 1;
 
-  if (!mgp_proc_add_result(error_proc, "error_result", mgp_type_string())) return 1;
+  const struct mgp_type *string_type = NULL;
+  const enum mgp_error string_type_err = mgp_type_string(&string_type);
+  if (string_type_err != MGP_ERROR_NO_ERROR) return 1;
+  if (mgp_proc_add_result(error_proc, "error_result", string_type) != MGP_ERROR_NO_ERROR) return 1;
 
-  struct mgp_proc *succ_proc = mgp_module_add_read_procedure(module, "success", success);
-  if (!succ_proc) return 1;
+  struct mgp_proc *succ_proc = NULL;
+  const enum mgp_error succ_proc_err = mgp_module_add_read_procedure(module, "success", success, &succ_proc);
+  if (succ_proc_err != MGP_ERROR_NO_ERROR) return 1;
 
-  if (!mgp_proc_add_result(succ_proc, "success_result", mgp_type_string())) return 1;
+  if (mgp_proc_add_result(succ_proc, "success_result", string_type) != MGP_ERROR_NO_ERROR) return 1;
 
   return 0;
 }
diff --git a/tests/unit/mgp_kafka_c_api.cpp b/tests/unit/mgp_kafka_c_api.cpp
index 117c7b9e6..8e1396075 100644
--- a/tests/unit/mgp_kafka_c_api.cpp
+++ b/tests/unit/mgp_kafka_c_api.cpp
@@ -9,6 +9,7 @@
 #include "gtest/gtest.h"
 #include "integrations/kafka/consumer.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
+#include "test_utils.hpp"
 #include "utils/pmr/vector.hpp"
 
 /// This class implements the interface of RdKafka::Message such that it can be mocked.
@@ -133,20 +134,21 @@ class MgpApiTest : public ::testing::Test {
 
 TEST_F(MgpApiTest, TestAllMgpKafkaCApi) {
   const mgp_messages &messages = Messages();
-  EXPECT_EQ(mgp_messages_size(&messages), expected.size());
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_messages_size, &messages), expected.size());
 
   for (int i = 0; i < expected.size(); ++i) {
-    const auto *message = mgp_messages_at(&messages, i);
+    const auto *message = EXPECT_MGP_NO_ERROR(const mgp_message *, mgp_messages_at, &messages, i);
     // Test for key and key size. Key size is always 1 in this test.
-    EXPECT_EQ(mgp_message_key_size(message), 1);
-    EXPECT_EQ(*mgp_message_key(message), expected[i].key);
+    EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_message_key_size, message), 1);
+    EXPECT_EQ(*EXPECT_MGP_NO_ERROR(const char *, mgp_message_key, message), expected[i].key);
 
     // Test for payload size
-    EXPECT_EQ(mgp_message_payload_size(message), expected[i].payload_size);
+    EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_message_payload_size, message), expected[i].payload_size);
     // Test for payload
-    EXPECT_FALSE(std::strcmp(mgp_message_payload(message), expected[i].payload));
+    EXPECT_FALSE(std::strcmp(EXPECT_MGP_NO_ERROR(const char *, mgp_message_payload, message), expected[i].payload));
     // Test for topic name
-    EXPECT_FALSE(std::strcmp(mgp_message_topic_name(message), expected[i].topic_name));
+    EXPECT_FALSE(
+        std::strcmp(EXPECT_MGP_NO_ERROR(const char *, mgp_message_topic_name, message), expected[i].topic_name));
   }
 
   // Unfortunately, we can't test timestamp here because we can't mock (as explained above)
diff --git a/tests/unit/mgp_trans_c_api.cpp b/tests/unit/mgp_trans_c_api.cpp
index 54051c079..150dfcc4e 100644
--- a/tests/unit/mgp_trans_c_api.cpp
+++ b/tests/unit/mgp_trans_c_api.cpp
@@ -13,13 +13,13 @@ TEST(MgpTransTest, TestMgpTransApi) {
   // for different string cases as these are all handled by
   // IsValidIdentifier().
   // Maybe add a mock instead and expect IsValidIdentifier() to be called once?
-  EXPECT_FALSE(mgp_module_add_transformation(&module, "dash-dash", no_op_cb));
-  EXPECT_TRUE(module.transformations.size() == 0);
+  EXPECT_EQ(mgp_module_add_transformation(&module, "dash-dash", no_op_cb), MGP_ERROR_INVALID_ARGUMENT);
+  EXPECT_TRUE(module.transformations.empty());
 
-  EXPECT_TRUE(mgp_module_add_transformation(&module, "transform", no_op_cb));
+  EXPECT_EQ(mgp_module_add_transformation(&module, "transform", no_op_cb), MGP_ERROR_NO_ERROR);
   EXPECT_NE(module.transformations.find("transform"), module.transformations.end());
 
   // Try to register a transformation twice
-  EXPECT_FALSE(mgp_module_add_transformation(&module, "transform", no_op_cb));
+  EXPECT_EQ(mgp_module_add_transformation(&module, "transform", no_op_cb), MGP_ERROR_LOGIC_ERROR);
   EXPECT_TRUE(module.transformations.size() == 1);
 }
diff --git a/tests/unit/query_procedure_mgp_module.cpp b/tests/unit/query_procedure_mgp_module.cpp
index 74e2da106..f01596bf4 100644
--- a/tests/unit/query_procedure_mgp_module.cpp
+++ b/tests/unit/query_procedure_mgp_module.cpp
@@ -11,34 +11,42 @@ static void DummyCallback(const mgp_list *, const mgp_graph *, mgp_result *, mgp
 
 TEST(Module, InvalidProcedureRegistration) {
   mgp_module module(utils::NewDeleteResource());
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "dashes-not-supported", DummyCallback));
+  mgp_proc *proc{nullptr};
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "dashes-not-supported", DummyCallback, &proc),
+            MGP_ERROR_INVALID_ARGUMENT);
   // as u8string this is u8"unicode\u22c6not\u2014supported"
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "unicode\xE2\x8B\x86not\xE2\x80\x94supported", DummyCallback));
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "unicode\xE2\x8B\x86not\xE2\x80\x94supported", DummyCallback, &proc),
+            MGP_ERROR_INVALID_ARGUMENT);
   // as u8string this is u8"`backticks⋆\u22c6won't-save\u2014you`"
-  EXPECT_FALSE(
-      mgp_module_add_read_procedure(&module, "`backticks⋆\xE2\x8B\x86won't-save\xE2\x80\x94you`", DummyCallback));
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "42_name_must_not_start_with_number", DummyCallback));
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "div/", DummyCallback));
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "mul*", DummyCallback));
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "question_mark_is_not_valid?", DummyCallback));
+  EXPECT_EQ(
+      mgp_module_add_read_procedure(&module, "`backticks⋆\xE2\x8B\x86won't-save\xE2\x80\x94you`", DummyCallback, &proc),
+      MGP_ERROR_INVALID_ARGUMENT);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "42_name_must_not_start_with_number", DummyCallback, &proc),
+            MGP_ERROR_INVALID_ARGUMENT);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "div/", DummyCallback, &proc), MGP_ERROR_INVALID_ARGUMENT);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "mul*", DummyCallback, &proc), MGP_ERROR_INVALID_ARGUMENT);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "question_mark_is_not_valid?", DummyCallback, &proc),
+            MGP_ERROR_INVALID_ARGUMENT);
 }
 
 TEST(Module, RegisteringTheSameProcedureMultipleTimes) {
   mgp_module module(utils::NewDeleteResource());
+  mgp_proc *proc{nullptr};
   EXPECT_EQ(module.procedures.find("same_name"), module.procedures.end());
-  EXPECT_TRUE(mgp_module_add_read_procedure(&module, "same_name", DummyCallback));
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "same_name", DummyCallback, &proc), MGP_ERROR_NO_ERROR);
   EXPECT_NE(module.procedures.find("same_name"), module.procedures.end());
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "same_name", DummyCallback));
-  EXPECT_FALSE(mgp_module_add_read_procedure(&module, "same_name", DummyCallback));
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "same_name", DummyCallback, &proc), MGP_ERROR_LOGIC_ERROR);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "same_name", DummyCallback, &proc), MGP_ERROR_LOGIC_ERROR);
   EXPECT_NE(module.procedures.find("same_name"), module.procedures.end());
 }
 
 TEST(Module, CaseSensitiveProcedureNames) {
   mgp_module module(utils::NewDeleteResource());
   EXPECT_TRUE(module.procedures.empty());
-  EXPECT_TRUE(mgp_module_add_read_procedure(&module, "not_same", DummyCallback));
-  EXPECT_TRUE(mgp_module_add_read_procedure(&module, "NoT_saME", DummyCallback));
-  EXPECT_TRUE(mgp_module_add_read_procedure(&module, "NOT_SAME", DummyCallback));
+  mgp_proc *proc{nullptr};
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "not_same", DummyCallback, &proc), MGP_ERROR_NO_ERROR);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "NoT_saME", DummyCallback, &proc), MGP_ERROR_NO_ERROR);
+  EXPECT_EQ(mgp_module_add_read_procedure(&module, "NOT_SAME", DummyCallback, &proc), MGP_ERROR_NO_ERROR);
   EXPECT_EQ(module.procedures.size(), 3U);
 }
 
@@ -51,29 +59,42 @@ static void CheckSignature(const mgp_proc *proc, const std::string &expected) {
 TEST(Module, ProcedureSignature) {
   mgp_memory memory{utils::NewDeleteResource()};
   mgp_module module(utils::NewDeleteResource());
-  auto *proc = mgp_module_add_read_procedure(&module, "proc", DummyCallback);
+  auto *proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "proc", &DummyCallback);
   CheckSignature(proc, "proc() :: ()");
-  mgp_proc_add_arg(proc, "arg1", mgp_type_number());
+  EXPECT_EQ(mgp_proc_add_arg(proc, "arg1", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)), MGP_ERROR_NO_ERROR);
   CheckSignature(proc, "proc(arg1 :: NUMBER) :: ()");
-  mgp_proc_add_opt_arg(proc, "opt1", mgp_type_nullable(mgp_type_any()),
-                       test_utils::CreateValueOwningPtr(mgp_value_make_null(&memory)).get());
+  EXPECT_EQ(
+      mgp_proc_add_opt_arg(
+          proc, "opt1",
+          EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
+          test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
+      MGP_ERROR_NO_ERROR);
   CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: ()");
-  mgp_proc_add_result(proc, "res1", mgp_type_list(mgp_type_int()));
+  EXPECT_EQ(mgp_proc_add_result(proc, "res1",
+                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
+                                                    EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int))),
+            MGP_ERROR_NO_ERROR);
   CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)");
-  EXPECT_FALSE(mgp_proc_add_arg(proc, "arg2", mgp_type_number()));
+  EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)),
+            MGP_ERROR_LOGIC_ERROR);
   CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)");
-  EXPECT_FALSE(mgp_proc_add_arg(proc, "arg2", mgp_type_map()));
-  CheckSignature(proc,
-                 "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: "
-                 "(res1 :: LIST OF INTEGER)");
-  mgp_proc_add_deprecated_result(proc, "res2", mgp_type_string());
+  EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)), MGP_ERROR_LOGIC_ERROR);
+  CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)");
+  EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string)),
+            MGP_ERROR_NO_ERROR);
   CheckSignature(proc,
                  "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: "
                  "(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)");
-  EXPECT_FALSE(mgp_proc_add_result(proc, "res2", mgp_type_any()));
-  EXPECT_FALSE(mgp_proc_add_deprecated_result(proc, "res1", mgp_type_any()));
-  mgp_proc_add_opt_arg(proc, "opt2", mgp_type_string(),
-                       test_utils::CreateValueOwningPtr(mgp_value_make_string("string=\"value\"", &memory)).get());
+  EXPECT_EQ(mgp_proc_add_result(proc, "res2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
+            MGP_ERROR_LOGIC_ERROR);
+  EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res1", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
+            MGP_ERROR_LOGIC_ERROR);
+  EXPECT_EQ(
+      mgp_proc_add_opt_arg(proc, "opt2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+                           test_utils::CreateValueOwningPtr(
+                               EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_string, "string=\"value\"", &memory))
+                               .get()),
+      MGP_ERROR_NO_ERROR);
   CheckSignature(proc,
                  "proc(arg1 :: NUMBER, opt1 = Null :: ANY?, "
                  "opt2 = \"string=\\\"value\\\"\" :: STRING) :: "
@@ -83,8 +104,12 @@ TEST(Module, ProcedureSignature) {
 TEST(Module, ProcedureSignatureOnlyOptArg) {
   mgp_memory memory{utils::NewDeleteResource()};
   mgp_module module(utils::NewDeleteResource());
-  auto *proc = mgp_module_add_read_procedure(&module, "proc", DummyCallback);
-  mgp_proc_add_opt_arg(proc, "opt1", mgp_type_nullable(mgp_type_any()),
-                       test_utils::CreateValueOwningPtr(mgp_value_make_null(&memory)).get());
+  auto *proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "proc", &DummyCallback);
+  EXPECT_EQ(
+      mgp_proc_add_opt_arg(
+          proc, "opt1",
+          EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
+          test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
+      MGP_ERROR_NO_ERROR);
   CheckSignature(proc, "proc(opt1 = Null :: ANY?) :: ()");
 }
diff --git a/tests/unit/query_procedure_mgp_type.cpp b/tests/unit/query_procedure_mgp_type.cpp
index 588a35a85..3c1a46120 100644
--- a/tests/unit/query_procedure_mgp_type.cpp
+++ b/tests/unit/query_procedure_mgp_type.cpp
@@ -9,46 +9,63 @@
 #include "test_utils.hpp"
 
 TEST(CypherType, PresentableNameSimpleTypes) {
-  EXPECT_EQ(mgp_type_any()->impl->GetPresentableName(), "ANY");
-  EXPECT_EQ(mgp_type_bool()->impl->GetPresentableName(), "BOOLEAN");
-  EXPECT_EQ(mgp_type_string()->impl->GetPresentableName(), "STRING");
-  EXPECT_EQ(mgp_type_int()->impl->GetPresentableName(), "INTEGER");
-  EXPECT_EQ(mgp_type_float()->impl->GetPresentableName(), "FLOAT");
-  EXPECT_EQ(mgp_type_number()->impl->GetPresentableName(), "NUMBER");
-  EXPECT_EQ(mgp_type_map()->impl->GetPresentableName(), "MAP");
-  EXPECT_EQ(mgp_type_node()->impl->GetPresentableName(), "NODE");
-  EXPECT_EQ(mgp_type_relationship()->impl->GetPresentableName(), "RELATIONSHIP");
-  EXPECT_EQ(mgp_type_path()->impl->GetPresentableName(), "PATH");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)->impl->GetPresentableName(), "ANY");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool)->impl->GetPresentableName(), "BOOLEAN");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string)->impl->GetPresentableName(), "STRING");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int)->impl->GetPresentableName(), "INTEGER");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float)->impl->GetPresentableName(), "FLOAT");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)->impl->GetPresentableName(), "NUMBER");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)->impl->GetPresentableName(), "MAP");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node)->impl->GetPresentableName(), "NODE");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship)->impl->GetPresentableName(), "RELATIONSHIP");
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)->impl->GetPresentableName(), "PATH");
 }
 
 TEST(CypherType, PresentableNameCompositeTypes) {
+  const mgp_type *any_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any);
   {
-    const auto *nullable_any = mgp_type_nullable(mgp_type_any());
+    const auto *nullable_any = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, any_type);
     EXPECT_EQ(nullable_any->impl->GetPresentableName(), "ANY?");
   }
   {
-    const auto *nullable_any = mgp_type_nullable(mgp_type_nullable(mgp_type_any()));
+    const auto *nullable_any =
+        EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
+                            EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, any_type)));
     EXPECT_EQ(nullable_any->impl->GetPresentableName(), "ANY?");
   }
   {
-    const auto *nullable_list = mgp_type_nullable(mgp_type_list(mgp_type_any()));
+    const auto *nullable_list = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
+                                                    EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, any_type));
     EXPECT_EQ(nullable_list->impl->GetPresentableName(), "LIST? OF ANY");
   }
   {
-    const auto *list_of_int = mgp_type_list(mgp_type_int());
+    const auto *list_of_int =
+        EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int));
     EXPECT_EQ(list_of_int->impl->GetPresentableName(), "LIST OF INTEGER");
   }
   {
-    const auto *list_of_nullable_path = mgp_type_list(mgp_type_nullable(mgp_type_path()));
+    const auto *list_of_nullable_path = EXPECT_MGP_NO_ERROR(
+        const mgp_type *, mgp_type_list,
+        EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)));
     EXPECT_EQ(list_of_nullable_path->impl->GetPresentableName(), "LIST OF PATH?");
   }
   {
-    const auto *list_of_list_of_map = mgp_type_list(mgp_type_list(mgp_type_map()));
+    const auto *list_of_list_of_map = EXPECT_MGP_NO_ERROR(
+        const mgp_type *, mgp_type_list,
+        EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)));
     EXPECT_EQ(list_of_list_of_map->impl->GetPresentableName(), "LIST OF LIST OF MAP");
   }
   {
-    const auto *nullable_list_of_nullable_list_of_nullable_string =
-        mgp_type_nullable(mgp_type_list(mgp_type_nullable(mgp_type_list(mgp_type_nullable(mgp_type_string())))));
+    const auto *nullable_list_of_nullable_list_of_nullable_string = EXPECT_MGP_NO_ERROR(
+        const mgp_type *, mgp_type_nullable,
+        EXPECT_MGP_NO_ERROR(
+            const mgp_type *, mgp_type_list,
+            EXPECT_MGP_NO_ERROR(
+                const mgp_type *, mgp_type_nullable,
+                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
+                                    EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
+                                                        EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string))))));
     EXPECT_EQ(nullable_list_of_nullable_list_of_nullable_string->impl->GetPresentableName(),
               "LIST? OF LIST? OF STRING?");
   }
@@ -57,17 +74,26 @@ TEST(CypherType, PresentableNameCompositeTypes) {
 TEST(CypherType, NullSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
   {
-    auto *mgp_null = mgp_value_make_null(&memory);
+    auto *mgp_null = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory);
     const query::TypedValue tv_null;
-    std::vector<const mgp_type *> primitive_types{
-        mgp_type_any(),    mgp_type_bool(), mgp_type_string(), mgp_type_int(),          mgp_type_float(),
-        mgp_type_number(), mgp_type_map(),  mgp_type_node(),   mgp_type_relationship(), mgp_type_path()};
+    std::vector<const mgp_type *> primitive_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+                                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)};
     for (const auto *primitive_type : primitive_types) {
       for (const auto *type :
-           {primitive_type, mgp_type_list(primitive_type), mgp_type_list(mgp_type_nullable(primitive_type))}) {
+           {primitive_type, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, primitive_type),
+            EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
+                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, primitive_type))}) {
         EXPECT_FALSE(type->impl->SatisfiesType(*mgp_null));
         EXPECT_FALSE(type->impl->SatisfiesType(tv_null));
-        const auto *null_type = mgp_type_nullable(type);
+        const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
         EXPECT_TRUE(null_type->impl->SatisfiesType(*mgp_null));
         EXPECT_TRUE(null_type->impl->SatisfiesType(tv_null));
       }
@@ -81,7 +107,7 @@ static void CheckSatisfiesTypesAndNullable(const mgp_value *mgp_val, const query
   for (const auto *type : types) {
     EXPECT_TRUE(type->impl->SatisfiesType(*mgp_val)) << type->impl->GetPresentableName();
     EXPECT_TRUE(type->impl->SatisfiesType(tv));
-    const auto *null_type = mgp_type_nullable(type);
+    const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
     EXPECT_TRUE(null_type->impl->SatisfiesType(*mgp_val)) << null_type->impl->GetPresentableName();
     EXPECT_TRUE(null_type->impl->SatisfiesType(tv));
   }
@@ -90,10 +116,13 @@ static void CheckSatisfiesTypesAndNullable(const mgp_value *mgp_val, const query
 static void CheckNotSatisfiesTypesAndListAndNullable(const mgp_value *mgp_val, const query::TypedValue &tv,
                                                      const std::vector<const mgp_type *> &elem_types) {
   for (const auto *elem_type : elem_types) {
-    for (const auto *type : {elem_type, mgp_type_list(elem_type), mgp_type_list(mgp_type_nullable(elem_type))}) {
+    for (const auto *type :
+         {elem_type, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, elem_type),
+          EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
+                              EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, elem_type))}) {
       EXPECT_FALSE(type->impl->SatisfiesType(*mgp_val)) << type->impl->GetPresentableName();
       EXPECT_FALSE(type->impl->SatisfiesType(tv));
-      const auto *null_type = mgp_type_nullable(type);
+      const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
       EXPECT_FALSE(null_type->impl->SatisfiesType(*mgp_val)) << null_type->impl->GetPresentableName();
       EXPECT_FALSE(null_type->impl->SatisfiesType(tv));
     }
@@ -102,59 +131,94 @@ static void CheckNotSatisfiesTypesAndListAndNullable(const mgp_value *mgp_val, c
 
 TEST(CypherType, BoolSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *mgp_bool = mgp_value_make_bool(1, &memory);
+  auto *mgp_bool = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_bool, 1, &memory);
   const query::TypedValue tv_bool(true);
-  CheckSatisfiesTypesAndNullable(mgp_bool, tv_bool, {mgp_type_any(), mgp_type_bool()});
-  CheckNotSatisfiesTypesAndListAndNullable(mgp_bool, tv_bool,
-                                           {mgp_type_string(), mgp_type_int(), mgp_type_float(), mgp_type_number(),
-                                            mgp_type_map(), mgp_type_node(), mgp_type_relationship(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_bool, tv_bool,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool)});
+  CheckNotSatisfiesTypesAndListAndNullable(
+      mgp_bool, tv_bool,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_bool);
 }
 
 TEST(CypherType, IntSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *mgp_int = mgp_value_make_int(42, &memory);
+  auto *mgp_int = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 42, &memory);
   const query::TypedValue tv_int(42);
-  CheckSatisfiesTypesAndNullable(mgp_int, tv_int, {mgp_type_any(), mgp_type_int(), mgp_type_number()});
-  CheckNotSatisfiesTypesAndListAndNullable(mgp_int, tv_int,
-                                           {mgp_type_bool(), mgp_type_string(), mgp_type_float(), mgp_type_map(),
-                                            mgp_type_node(), mgp_type_relationship(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_int, tv_int,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)});
+  CheckNotSatisfiesTypesAndListAndNullable(
+      mgp_int, tv_int,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_int);
 }
 
 TEST(CypherType, DoubleSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *mgp_double = mgp_value_make_double(42, &memory);
+  auto *mgp_double = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, 42, &memory);
   const query::TypedValue tv_double(42.0);
-  CheckSatisfiesTypesAndNullable(mgp_double, tv_double, {mgp_type_any(), mgp_type_float(), mgp_type_number()});
-  CheckNotSatisfiesTypesAndListAndNullable(mgp_double, tv_double,
-                                           {mgp_type_bool(), mgp_type_string(), mgp_type_int(), mgp_type_map(),
-                                            mgp_type_node(), mgp_type_relationship(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_double, tv_double,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)});
+  CheckNotSatisfiesTypesAndListAndNullable(
+      mgp_double, tv_double,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_double);
 }
 
 TEST(CypherType, StringSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *mgp_string = mgp_value_make_string("text", &memory);
+  auto *mgp_string = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_string, "text", &memory);
   const query::TypedValue tv_string("text");
-  CheckSatisfiesTypesAndNullable(mgp_string, tv_string, {mgp_type_any(), mgp_type_string()});
-  CheckNotSatisfiesTypesAndListAndNullable(mgp_string, tv_string,
-                                           {mgp_type_bool(), mgp_type_int(), mgp_type_float(), mgp_type_number(),
-                                            mgp_type_map(), mgp_type_node(), mgp_type_relationship(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_string, tv_string,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string)});
+  CheckNotSatisfiesTypesAndListAndNullable(
+      mgp_string, tv_string,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_string);
 }
 
 TEST(CypherType, MapSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *map = mgp_map_make_empty(&memory);
-  mgp_map_insert(map, "key", test_utils::CreateValueOwningPtr(mgp_value_make_int(42, &memory)).get());
-  auto *mgp_map_v = mgp_value_make_map(map);
+  auto *map = EXPECT_MGP_NO_ERROR(mgp_map *, mgp_map_make_empty, &memory);
+  EXPECT_EQ(
+      mgp_map_insert(
+          map, "key",
+          test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 42, &memory)).get()),
+      MGP_ERROR_NO_ERROR);
+  auto *mgp_map_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_map, map);
   const query::TypedValue tv_map(std::map<std::string, query::TypedValue>{{"key", query::TypedValue(42)}});
-  CheckSatisfiesTypesAndNullable(mgp_map_v, tv_map, {mgp_type_any(), mgp_type_map()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_map_v, tv_map,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)});
   CheckNotSatisfiesTypesAndListAndNullable(
       mgp_map_v, tv_map,
-      {mgp_type_bool(), mgp_type_string(), mgp_type_int(), mgp_type_float(), mgp_type_number(), mgp_type_node(),
-       mgp_type_relationship(), mgp_type_path()});
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_map_v);
 }
 
@@ -166,12 +230,20 @@ TEST(CypherType, VertexSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
   utils::Allocator<mgp_vertex> alloc(memory.impl);
   mgp_graph graph{&dba, storage::View::NEW};
-  auto *mgp_vertex_v = mgp_value_make_vertex(alloc.new_object<mgp_vertex>(vertex, &graph));
+  auto *mgp_vertex_v =
+      EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_vertex, alloc.new_object<mgp_vertex>(vertex, &graph));
   const query::TypedValue tv_vertex(vertex);
-  CheckSatisfiesTypesAndNullable(mgp_vertex_v, tv_vertex, {mgp_type_any(), mgp_type_node(), mgp_type_map()});
-  CheckNotSatisfiesTypesAndListAndNullable(mgp_vertex_v, tv_vertex,
-                                           {mgp_type_bool(), mgp_type_string(), mgp_type_int(), mgp_type_float(),
-                                            mgp_type_number(), mgp_type_relationship(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_vertex_v, tv_vertex,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)});
+  CheckNotSatisfiesTypesAndListAndNullable(
+      mgp_vertex_v, tv_vertex,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_vertex_v);
 }
 
@@ -185,12 +257,18 @@ TEST(CypherType, EdgeSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
   utils::Allocator<mgp_edge> alloc(memory.impl);
   mgp_graph graph{&dba, storage::View::NEW};
-  auto *mgp_edge_v = mgp_value_make_edge(alloc.new_object<mgp_edge>(edge, &graph));
+  auto *mgp_edge_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_edge, alloc.new_object<mgp_edge>(edge, &graph));
   const query::TypedValue tv_edge(edge);
-  CheckSatisfiesTypesAndNullable(mgp_edge_v, tv_edge, {mgp_type_any(), mgp_type_relationship(), mgp_type_map()});
-  CheckNotSatisfiesTypesAndListAndNullable(mgp_edge_v, tv_edge,
-                                           {mgp_type_bool(), mgp_type_string(), mgp_type_int(), mgp_type_float(),
-                                            mgp_type_number(), mgp_type_node(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(mgp_edge_v, tv_edge,
+                                 {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
+                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+                                  EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)});
+  CheckNotSatisfiesTypesAndListAndNullable(
+      mgp_edge_v, tv_edge,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_edge_v);
 }
 
@@ -205,19 +283,24 @@ TEST(CypherType, PathSatisfiesType) {
   utils::Allocator<mgp_path> alloc(memory.impl);
   mgp_graph graph{&dba, storage::View::NEW};
   auto *mgp_vertex_v = alloc.new_object<mgp_vertex>(v1, &graph);
-  auto path = mgp_path_make_with_start(mgp_vertex_v, &memory);
+  auto path = EXPECT_MGP_NO_ERROR(mgp_path *, mgp_path_make_with_start, mgp_vertex_v, &memory);
   ASSERT_TRUE(path);
   alloc.delete_object(mgp_vertex_v);
   auto mgp_edge_v = alloc.new_object<mgp_edge>(edge, &graph);
-  ASSERT_TRUE(mgp_path_expand(path, mgp_edge_v));
+  ASSERT_EQ(mgp_path_expand(path, mgp_edge_v), MGP_ERROR_NO_ERROR);
   alloc.delete_object(mgp_edge_v);
-  auto *mgp_path_v = mgp_value_make_path(path);
+  auto *mgp_path_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_path, path);
   const query::TypedValue tv_path(query::Path(v1, edge, v2));
-  CheckSatisfiesTypesAndNullable(mgp_path_v, tv_path, {mgp_type_any(), mgp_type_path()});
+  CheckSatisfiesTypesAndNullable(
+      mgp_path_v, tv_path,
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   CheckNotSatisfiesTypesAndListAndNullable(
       mgp_path_v, tv_path,
-      {mgp_type_bool(), mgp_type_string(), mgp_type_int(), mgp_type_float(), mgp_type_number(), mgp_type_map(),
-       mgp_type_node(), mgp_type_relationship()});
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship)});
   mgp_value_destroy(mgp_path_v);
 }
 
@@ -225,23 +308,31 @@ static std::vector<const mgp_type *> MakeListTypes(const std::vector<const mgp_t
   std::vector<const mgp_type *> list_types;
   list_types.reserve(2U * element_types.size());
   for (const auto *type : element_types) {
-    list_types.push_back(mgp_type_list(type));
-    list_types.push_back(mgp_type_list(mgp_type_nullable(type)));
+    list_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, type));
+    list_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
+                                             EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type)));
   }
   return list_types;
 }
 
 TEST(CypherType, EmptyListSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *list = mgp_list_make_empty(0, &memory);
-  auto *mgp_list_v = mgp_value_make_list(list);
+  auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, 0, &memory);
+  auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
   query::TypedValue tv_list(std::vector<query::TypedValue>{});
   // Empty List satisfies all list element types
-  std::vector<const mgp_type *> primitive_types{
-      mgp_type_any(),    mgp_type_bool(), mgp_type_string(), mgp_type_int(),          mgp_type_float(),
-      mgp_type_number(), mgp_type_map(),  mgp_type_node(),   mgp_type_relationship(), mgp_type_path()};
+  std::vector<const mgp_type *> primitive_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)};
   auto all_types = MakeListTypes(primitive_types);
-  all_types.push_back(mgp_type_any());
+  all_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any));
   CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, all_types);
   mgp_value_destroy(mgp_list_v);
 }
@@ -249,18 +340,28 @@ TEST(CypherType, EmptyListSatisfiesType) {
 TEST(CypherType, ListOfIntSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
   constexpr int64_t elem_count = 3;
-  auto *list = mgp_list_make_empty(elem_count, &memory);
-  auto *mgp_list_v = mgp_value_make_list(list);
+  auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, elem_count, &memory);
+  auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
   query::TypedValue tv_list(std::vector<query::TypedValue>{});
   for (int64_t i = 0; i < elem_count; ++i) {
-    ASSERT_TRUE(mgp_list_append(list, test_utils::CreateValueOwningPtr(mgp_value_make_int(i, &memory)).get()));
+    ASSERT_EQ(
+        mgp_list_append(
+            list,
+            test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, i, &memory)).get()),
+        MGP_ERROR_NO_ERROR);
     tv_list.ValueList().emplace_back(i);
-    auto valid_types = MakeListTypes({mgp_type_any(), mgp_type_int(), mgp_type_number()});
-    valid_types.push_back(mgp_type_any());
+    auto valid_types = MakeListTypes({EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
+                                      EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+                                      EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)});
+    valid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any));
     CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, valid_types);
-    CheckNotSatisfiesTypesAndListAndNullable(mgp_list_v, tv_list,
-                                             {mgp_type_bool(), mgp_type_string(), mgp_type_float(), mgp_type_map(),
-                                              mgp_type_node(), mgp_type_relationship(), mgp_type_path()});
+    CheckNotSatisfiesTypesAndListAndNullable(
+        mgp_list_v, tv_list,
+        {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+         EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+         EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+         EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+         EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   }
   mgp_value_destroy(mgp_list_v);
 }
@@ -268,52 +369,75 @@ TEST(CypherType, ListOfIntSatisfiesType) {
 TEST(CypherType, ListOfIntAndBoolSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
   constexpr int64_t elem_count = 2;
-  auto *list = mgp_list_make_empty(elem_count, &memory);
-  auto *mgp_list_v = mgp_value_make_list(list);
+  auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, elem_count, &memory);
+  auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
   query::TypedValue tv_list(std::vector<query::TypedValue>{});
   // Add an int
-  ASSERT_TRUE(mgp_list_append(list, test_utils::CreateValueOwningPtr(mgp_value_make_int(42, &memory)).get()));
+  ASSERT_EQ(
+      mgp_list_append(
+          list,
+          test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 42, &memory)).get()),
+      MGP_ERROR_NO_ERROR);
   tv_list.ValueList().emplace_back(42);
   // Add a boolean
-  ASSERT_TRUE(mgp_list_append(list, test_utils::CreateValueOwningPtr(mgp_value_make_bool(1, &memory)).get()));
+  ASSERT_EQ(
+      mgp_list_append(
+          list,
+          test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_bool, 1, &memory)).get()),
+      MGP_ERROR_NO_ERROR);
   tv_list.ValueList().emplace_back(true);
-  auto valid_types = MakeListTypes({mgp_type_any()});
-  valid_types.push_back(mgp_type_any());
+  auto valid_types = MakeListTypes({EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)});
+  valid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any));
   CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, valid_types);
   // All other types will not be satisfied
   CheckNotSatisfiesTypesAndListAndNullable(
       mgp_list_v, tv_list,
-      {mgp_type_bool(), mgp_type_string(), mgp_type_int(), mgp_type_float(), mgp_type_number(), mgp_type_map(),
-       mgp_type_node(), mgp_type_relationship(), mgp_type_path()});
+      {EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+       EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
   mgp_value_destroy(mgp_list_v);
 }
 
 TEST(CypherType, ListOfNullSatisfiesType) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *list = mgp_list_make_empty(1, &memory);
-  auto *mgp_list_v = mgp_value_make_list(list);
+  auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, 1, &memory);
+  auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
   query::TypedValue tv_list(std::vector<query::TypedValue>{});
-  ASSERT_TRUE(mgp_list_append(list, test_utils::CreateValueOwningPtr(mgp_value_make_null(&memory)).get()));
+  ASSERT_EQ(
+      mgp_list_append(
+          list, test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
+      MGP_ERROR_NO_ERROR);
   tv_list.ValueList().emplace_back();
   // List with Null satisfies all nullable list element types
-  std::vector<const mgp_type *> primitive_types{
-      mgp_type_any(),    mgp_type_bool(), mgp_type_string(), mgp_type_int(),          mgp_type_float(),
-      mgp_type_number(), mgp_type_map(),  mgp_type_node(),   mgp_type_relationship(), mgp_type_path()};
-  std::vector<const mgp_type *> valid_types{mgp_type_any()};
+  std::vector<const mgp_type *> primitive_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
+                                                EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)};
+  std::vector<const mgp_type *> valid_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)};
   valid_types.reserve(1U + primitive_types.size());
   for (const auto *elem_type : primitive_types) {
-    valid_types.push_back(mgp_type_list(mgp_type_nullable(elem_type)));
+    valid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
+                                              EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, elem_type)));
   }
   CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, valid_types);
   std::vector<const mgp_type *> invalid_types;
   invalid_types.reserve(primitive_types.size());
   for (const auto *elem_type : primitive_types) {
-    invalid_types.push_back(mgp_type_list(elem_type));
+    invalid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, elem_type));
   }
   for (const auto *type : invalid_types) {
     EXPECT_FALSE(type->impl->SatisfiesType(*mgp_list_v)) << type->impl->GetPresentableName();
     EXPECT_FALSE(type->impl->SatisfiesType(tv_list));
-    const auto *null_type = mgp_type_nullable(type);
+    const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
     EXPECT_FALSE(null_type->impl->SatisfiesType(*mgp_list_v)) << null_type->impl->GetPresentableName();
     EXPECT_FALSE(null_type->impl->SatisfiesType(tv_list));
   }
diff --git a/tests/unit/query_procedure_py_module.cpp b/tests/unit/query_procedure_py_module.cpp
index 95a7af2a1..9602c2972 100644
--- a/tests/unit/query_procedure_py_module.cpp
+++ b/tests/unit/query_procedure_py_module.cpp
@@ -5,25 +5,29 @@
 
 #include "query/procedure/mg_procedure_impl.hpp"
 #include "query/procedure/py_module.hpp"
+#include "test_utils.hpp"
 
 TEST(PyModule, MgpValueToPyObject) {
   mgp_memory memory{utils::NewDeleteResource()};
-  auto *list = mgp_list_make_empty(42, &memory);
+  auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, 42, &memory);
   {
     // Create a list: [null, false, true, 42, 0.1, "some text"]
-    auto primitive_values = {mgp_value_make_null(&memory),        mgp_value_make_bool(0, &memory),
-                             mgp_value_make_bool(1, &memory),     mgp_value_make_int(42, &memory),
-                             mgp_value_make_double(0.1, &memory), mgp_value_make_string("some text", &memory)};
+    auto primitive_values = {EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory),
+                             EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_bool, 0, &memory),
+                             EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_bool, 1, &memory),
+                             EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 42, &memory),
+                             EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, 0.1, &memory),
+                             EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_string, "some text", &memory)};
     for (auto *val : primitive_values) {
-      mgp_list_append(list, val);
+      EXPECT_EQ(mgp_list_append(list, val), MGP_ERROR_NO_ERROR);
       mgp_value_destroy(val);
     }
   }
-  auto *list_val = mgp_value_make_list(list);
-  auto *map = mgp_map_make_empty(&memory);
-  mgp_map_insert(map, "list", list_val);
+  auto *list_val = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
+  auto *map = EXPECT_MGP_NO_ERROR(mgp_map *, mgp_map_make_empty, &memory);
+  EXPECT_EQ(mgp_map_insert(map, "list", list_val), MGP_ERROR_NO_ERROR);
   mgp_value_destroy(list_val);
-  auto *map_val = mgp_value_make_map(map);
+  auto *map_val = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_map, map);
   auto gil = py::EnsureGIL();
   py::Object py_graph(query::procedure::MakePyGraph(nullptr, &memory));
   auto py_dict =
@@ -99,9 +103,10 @@ TEST(PyModule, PyVertex) {
   query::DbAccessor dba(&storage_dba);
   mgp_memory memory{utils::NewDeleteResource()};
   mgp_graph graph{&dba, storage::View::OLD};
-  auto *vertex = mgp_graph_get_vertex_by_id(&graph, mgp_vertex_id{0}, &memory);
+  auto *vertex = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
   ASSERT_TRUE(vertex);
-  auto *vertex_value = mgp_value_make_vertex(mgp_vertex_copy(vertex, &memory));
+  auto *vertex_value = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_vertex,
+                                           EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_vertex_copy, vertex, &memory));
   mgp_vertex_destroy(vertex);
   // Initialize the Python graph object.
   auto gil = py::EnsureGIL();
@@ -116,8 +121,11 @@ TEST(PyModule, PyVertex) {
   // Test for equality.
   ASSERT_TRUE(new_vertex_value);
   ASSERT_NE(new_vertex_value, vertex_value);  // Pointer compare.
-  ASSERT_TRUE(mgp_value_is_vertex(new_vertex_value));
-  ASSERT_TRUE(mgp_vertex_equal(mgp_value_get_vertex(vertex_value), mgp_value_get_vertex(new_vertex_value)));
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_vertex, new_vertex_value), 1);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_equal,
+                                EXPECT_MGP_NO_ERROR(const mgp_vertex *, mgp_value_get_vertex, vertex_value),
+                                EXPECT_MGP_NO_ERROR(const mgp_vertex *, mgp_value_get_vertex, new_vertex_value)),
+            1);
   // Clean up.
   mgp_value_destroy(new_vertex_value);
   mgp_value_destroy(vertex_value);
@@ -144,14 +152,15 @@ TEST(PyModule, PyEdge) {
   query::DbAccessor dba(&storage_dba);
   mgp_memory memory{utils::NewDeleteResource()};
   mgp_graph graph{&dba, storage::View::OLD};
-  auto *start_v = mgp_graph_get_vertex_by_id(&graph, mgp_vertex_id{0}, &memory);
+  auto *start_v = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
   ASSERT_TRUE(start_v);
-  auto *edges_it = mgp_vertex_iter_out_edges(start_v, &memory);
+  auto *edges_it = EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, start_v, &memory);
   ASSERT_TRUE(edges_it);
-  auto *edge = mgp_edge_copy(mgp_edges_iterator_get(edges_it), &memory);
-  auto *edge_value = mgp_value_make_edge(edge);
-  mgp_edges_iterator_next(edges_it);
-  ASSERT_EQ(mgp_edges_iterator_get(edges_it), nullptr);
+  auto *edge = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy,
+                                   EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_get, edges_it), &memory);
+  auto *edge_value = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_edge, edge);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_next, edges_it), nullptr);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_get, edges_it), nullptr);
   mgp_edges_iterator_destroy(edges_it);
   mgp_vertex_destroy(start_v);
   // Initialize the Python graph object.
@@ -167,8 +176,11 @@ TEST(PyModule, PyEdge) {
   // Test for equality.
   ASSERT_TRUE(new_edge_value);
   ASSERT_NE(new_edge_value, edge_value);  // Pointer compare.
-  ASSERT_TRUE(mgp_value_is_edge(new_edge_value));
-  ASSERT_TRUE(mgp_edge_equal(mgp_value_get_edge(edge_value), mgp_value_get_edge(new_edge_value)));
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_edge, new_edge_value), 1);
+  ASSERT_EQ(
+      EXPECT_MGP_NO_ERROR(int, mgp_edge_equal, EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_value_get_edge, edge_value),
+                          EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_value_get_edge, new_edge_value)),
+      1);
   // Clean up.
   mgp_value_destroy(new_edge_value);
   mgp_value_destroy(edge_value);
@@ -188,19 +200,20 @@ TEST(PyModule, PyPath) {
   query::DbAccessor dba(&storage_dba);
   mgp_memory memory{utils::NewDeleteResource()};
   mgp_graph graph{&dba, storage::View::OLD};
-  auto *start_v = mgp_graph_get_vertex_by_id(&graph, mgp_vertex_id{0}, &memory);
+  auto *start_v = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
   ASSERT_TRUE(start_v);
-  auto *path = mgp_path_make_with_start(start_v, &memory);
+  auto *path = EXPECT_MGP_NO_ERROR(mgp_path *, mgp_path_make_with_start, start_v, &memory);
   ASSERT_TRUE(path);
-  auto *edges_it = mgp_vertex_iter_out_edges(start_v, &memory);
+  auto *edges_it = EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, start_v, &memory);
   ASSERT_TRUE(edges_it);
-  for (const auto *edge = mgp_edges_iterator_get(edges_it); edge; edge = mgp_edges_iterator_next(edges_it)) {
-    ASSERT_TRUE(mgp_path_expand(path, edge));
+  for (const auto *edge = EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_get, edges_it); edge != nullptr;
+       edge = EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_next, edges_it)) {
+    ASSERT_EQ(mgp_path_expand(path, edge), MGP_ERROR_NO_ERROR);
   }
-  ASSERT_EQ(mgp_path_size(path), 1);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_path_size, path), 1);
   mgp_edges_iterator_destroy(edges_it);
   mgp_vertex_destroy(start_v);
-  auto *path_value = mgp_value_make_path(path);
+  auto *path_value = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_path, path);
   ASSERT_TRUE(path_value);
   auto gil = py::EnsureGIL();
   py::Object py_graph(query::procedure::MakePyGraph(&graph, &memory));
@@ -213,8 +226,11 @@ TEST(PyModule, PyPath) {
   auto *new_path_value = query::procedure::PyObjectToMgpValue(py_path_value.Ptr(), &memory);
   ASSERT_TRUE(new_path_value);
   ASSERT_NE(new_path_value, path_value);  // Pointer compare.
-  ASSERT_TRUE(mgp_value_is_path(new_path_value));
-  ASSERT_TRUE(mgp_path_equal(mgp_value_get_path(path_value), mgp_value_get_path(new_path_value)));
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_path, new_path_value), 1);
+  ASSERT_EQ(
+      EXPECT_MGP_NO_ERROR(int, mgp_path_equal, EXPECT_MGP_NO_ERROR(const mgp_path *, mgp_value_get_path, path_value),
+                          EXPECT_MGP_NO_ERROR(const mgp_path *, mgp_value_get_path, new_path_value)),
+      1);
   mgp_value_destroy(new_path_value);
   mgp_value_destroy(path_value);
   ASSERT_FALSE(dba.Commit().HasError());
@@ -225,35 +241,57 @@ TEST(PyModule, PyObjectToMgpValue) {
   auto gil = py::EnsureGIL();
   py::Object py_value{
       Py_BuildValue("[i f s (i f s) {s i s f}]", 1, 1.0, "one", 2, 2.0, "two", "three", 3, "four", 4.0)};
-  mgp_value *value = query::procedure::PyObjectToMgpValue(py_value.Ptr(), &memory);
+  auto *value = query::procedure::PyObjectToMgpValue(py_value.Ptr(), &memory);
 
-  ASSERT_TRUE(mgp_value_is_list(value));
-  const mgp_list *list1 = mgp_value_get_list(value);
-  EXPECT_EQ(mgp_list_size(list1), 5);
-  ASSERT_TRUE(mgp_value_is_int(mgp_list_at(list1, 0)));
-  EXPECT_EQ(mgp_value_get_int(mgp_list_at(list1, 0)), 1);
-  ASSERT_TRUE(mgp_value_is_double(mgp_list_at(list1, 1)));
-  EXPECT_EQ(mgp_value_get_double(mgp_list_at(list1, 1)), 1.0);
-  ASSERT_TRUE(mgp_value_is_string(mgp_list_at(list1, 2)));
-  EXPECT_STREQ(mgp_value_get_string(mgp_list_at(list1, 2)), "one");
-  ASSERT_TRUE(mgp_value_is_list(mgp_list_at(list1, 3)));
-  const mgp_list *list2 = mgp_value_get_list(mgp_list_at(list1, 3));
-  EXPECT_EQ(mgp_list_size(list2), 3);
-  ASSERT_TRUE(mgp_value_is_int(mgp_list_at(list2, 0)));
-  EXPECT_EQ(mgp_value_get_int(mgp_list_at(list2, 0)), 2);
-  ASSERT_TRUE(mgp_value_is_double(mgp_list_at(list2, 1)));
-  EXPECT_EQ(mgp_value_get_double(mgp_list_at(list2, 1)), 2.0);
-  ASSERT_TRUE(mgp_value_is_string(mgp_list_at(list2, 2)));
-  EXPECT_STREQ(mgp_value_get_string(mgp_list_at(list2, 2)), "two");
-  ASSERT_TRUE(mgp_value_is_map(mgp_list_at(list1, 4)));
-  const mgp_map *map = mgp_value_get_map(mgp_list_at(list1, 4));
-  EXPECT_EQ(mgp_map_size(map), 2);
-  const mgp_value *v1 = mgp_map_at(map, "three");
-  ASSERT_TRUE(mgp_value_is_int(v1));
-  EXPECT_EQ(mgp_value_get_int(v1), 3);
-  const mgp_value *v2 = mgp_map_at(map, "four");
-  ASSERT_TRUE(mgp_value_is_double(v2));
-  EXPECT_EQ(mgp_value_get_double(v2), 4.0);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_list, value), 1);
+  auto *list1 = EXPECT_MGP_NO_ERROR(const mgp_list *, mgp_value_get_list, value);
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_list_size, list1), 5);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 0)),
+            1);
+  EXPECT_EQ(
+      EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 0)),
+      1);
+  ASSERT_EQ(
+      EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 1)), 1);
+  EXPECT_EQ(
+      EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 1)),
+      1.0);
+  ASSERT_EQ(
+      EXPECT_MGP_NO_ERROR(int, mgp_value_is_string, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 2)), 1);
+  EXPECT_STREQ(EXPECT_MGP_NO_ERROR(const char *, mgp_value_get_string,
+                                   EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 2)),
+               "one");
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_list, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 3)),
+            1);
+  auto *list2 = EXPECT_MGP_NO_ERROR(const mgp_list *, mgp_value_get_list,
+                                    EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 3));
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_list_size, list2), 3);
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 0)),
+            1);
+  EXPECT_EQ(
+      EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 0)),
+      2);
+  ASSERT_EQ(
+      EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 1)), 1);
+  EXPECT_EQ(
+      EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 1)),
+      2.0);
+  ASSERT_EQ(
+      EXPECT_MGP_NO_ERROR(int, mgp_value_is_string, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 2)), 1);
+  EXPECT_STREQ(EXPECT_MGP_NO_ERROR(const char *, mgp_value_get_string,
+                                   EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 2)),
+               "two");
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_map, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 4)),
+            1);
+  auto *map = EXPECT_MGP_NO_ERROR(const mgp_map *, mgp_value_get_map,
+                                  EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 4));
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_map_size, map), 2);
+  const mgp_value *v1 = EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_map_at, map, "three");
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, v1), 1);
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, v1), 3);
+  const mgp_value *v2 = EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_map_at, map, "four");
+  ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, v2), 1);
+  EXPECT_EQ(EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, v2), 4.0);
   mgp_value_destroy(value);
 }
 
diff --git a/tests/unit/test_utils.hpp b/tests/unit/test_utils.hpp
index 4efc0f4b2..36591d97e 100644
--- a/tests/unit/test_utils.hpp
+++ b/tests/unit/test_utils.hpp
@@ -1,9 +1,26 @@
+#pragma once
+
 #include <memory>
 
+#include <fmt/format.h>
+#include <gtest/gtest.h>
+
+#include "mg_procedure.h"
 #include "query/procedure/mg_procedure_impl.hpp"
 
 namespace test_utils {
 using MgpValueOwningPtr = std::unique_ptr<mgp_value, void (*)(mgp_value *)>;
 
 MgpValueOwningPtr CreateValueOwningPtr(mgp_value *value) { return MgpValueOwningPtr(value, &mgp_value_destroy); }
+
+template <typename TResult, typename TFunc, typename... TArgs>
+TResult ExpectNoError(const char *file, int line, TFunc func, TArgs &&...args) {
+  static_assert(std::is_trivially_copyable_v<TFunc>);
+  static_assert((std::is_trivially_copyable_v<std::remove_reference_t<TArgs>> && ...));
+  TResult result{};
+  EXPECT_EQ(func(args..., &result), MGP_ERROR_NO_ERROR) << fmt::format("Source of error: {} at line {}", file, line);
+  return result;
+}
 }  // namespace test_utils
+
+#define EXPECT_MGP_NO_ERROR(type, ...) test_utils::ExpectNoError<type>(__FILE__, __LINE__, __VA_ARGS__)
\ No newline at end of file