Repository: man-group/ArcticDB (language: python)

Issue:
Clearer error messages for mismatched types
**Is your feature request related to a problem? Please describe.**
- Create a dataframe with a string column representing a numeric identifier
- Query the column using QueryBuilder, but mistakenly using an int in the filter

The user gets an error like,

```
File "/users/isys/glai/pyenvs/res-new/lib/python3.6/site-packages/arcticdb/version_store/_store.py", line 1686, in _read_dataframe
    return ReadResult(*self.version_store.read_dataframe_version(symbol, version_query, read_query, read_options))
arcticdb_ext.exceptions.InternalException: E_ASSERTION_FAILURE Cannot compare TD<type=UTF_DYNAMIC64, dim=0> to TD<type=UINT32, dim=0> (possible categorical?)
```

**Describe the solution you'd like**
- We should translate the message in to "user-space" a bit more. We can't expect users to translate UTF_DYNAMIC64 in to "Python string"
- We don't tell the user the affected column (slightly relates to #944 )

Clearer error messages for mismatched types
**Is your feature request related to a problem? Please describe.**
- Create a dataframe with a string column representing a numeric identifier
- Query the column using QueryBuilder, but mistakenly using an int in the filter

The user gets an error like,

```
File "/users/isys/glai/pyenvs/res-new/lib/python3.6/site-packages/arcticdb/version_store/_store.py", line 1686, in _read_dataframe
    return ReadResult(*self.version_store.read_dataframe_version(symbol, version_query, read_query, read_options))
arcticdb_ext.exceptions.InternalException: E_ASSERTION_FAILURE Cannot compare TD<type=UTF_DYNAMIC64, dim=0> to TD<type=UINT32, dim=0> (possible categorical?)
```

**Describe the solution you'd like**
- We should translate the message in to "user-space" a bit more. We can't expect users to translate UTF_DYNAMIC64 in to "Python string"
- We don't tell the user the affected column (slightly relates to #944 )



Discussion / hints:

Fix patch:
diff --git a/cpp/arcticdb/entity/type_utils.hpp b/cpp/arcticdb/entity/type_utils.hpp
index 4ecc0f2606..e370754bf4 100644
--- a/cpp/arcticdb/entity/type_utils.hpp
+++ b/cpp/arcticdb/entity/type_utils.hpp
@@ -7,7 +7,9 @@
 
 #pragma once
 #include<optional>
+#include <fmt/format.h>
 #include<arcticdb/entity/descriptors.hpp>
+#include <arcticdb/entity/types.hpp>
 
 namespace arcticdb {
 
@@ -38,4 +40,9 @@ namespace entity {
     const proto::descriptors::TypeDescriptor& left,
     const proto::descriptors::TypeDescriptor& right
 );
+
+inline std::string get_user_friendly_type_string(const entity::TypeDescriptor& type) {
+    return is_sequence_type(type.data_type()) ? fmt::format("TD<type=STRING, dim={}>", type.dimension_) : fmt::format("{}", type);
+}
+
 }
diff --git a/cpp/arcticdb/pipeline/value.hpp b/cpp/arcticdb/pipeline/value.hpp
index 7d9e20153c..b3dbadd4c0 100644
--- a/cpp/arcticdb/pipeline/value.hpp
+++ b/cpp/arcticdb/pipeline/value.hpp
@@ -132,6 +132,16 @@ struct Value {
         *str_data() = data;
     }
 
+    template<typename RawType>
+    std::string to_string() const {
+        if (has_sequence_type()) {
+            return "\"" + std::string(*str_data(), len()) + "\"";
+        }
+        else {
+            return fmt::format("{}", get<RawType>());
+        }
+    }
+
     ~Value() {
         if(has_sequence_type())
             delete[] *str_data();
@@ -208,4 +218,4 @@ inline std::optional<std::string> ascii_to_padded_utf32(std::string_view str, si
     return rv;
 }
 
-} //namespace arcticdb
\ No newline at end of file
+} //namespace arcticdb
diff --git a/cpp/arcticdb/processing/expression_node.hpp b/cpp/arcticdb/processing/expression_node.hpp
index dc55e2beb4..af47bcd15f 100644
--- a/cpp/arcticdb/processing/expression_node.hpp
+++ b/cpp/arcticdb/processing/expression_node.hpp
@@ -14,6 +14,7 @@
 #include <arcticdb/processing/operation_types.hpp>
 #include <arcticdb/pipeline/value.hpp>
 #include <arcticdb/pipeline/value_set.hpp>
+#include <utility>
 
 
 namespace arcticdb {
@@ -42,19 +43,23 @@ class StringPool;
 struct ColumnWithStrings {
     std::shared_ptr<Column> column_;
     const std::shared_ptr<StringPool> string_pool_;
+    std::string column_name_;
 
-    explicit ColumnWithStrings(std::unique_ptr<Column>&& col) :
-        column_(std::move(col)) {
+    ColumnWithStrings(std::unique_ptr<Column>&& col, std::string_view col_name) :
+        column_(std::move(col)),
+        column_name_(col_name) {
     }
 
-    ColumnWithStrings(Column&& col, std::shared_ptr<StringPool> string_pool) :
+    ColumnWithStrings(Column&& col, std::shared_ptr<StringPool> string_pool, std::string_view col_name) :
         column_(std::make_shared<Column>(std::move(col))),
-        string_pool_(std::move(string_pool)) {
+        string_pool_(std::move(string_pool)),
+        column_name_(col_name) {
     }
 
-    ColumnWithStrings(std::shared_ptr<Column> column, const std::shared_ptr<StringPool>& string_pool) :
+    ColumnWithStrings(std::shared_ptr<Column> column, const std::shared_ptr<StringPool>& string_pool, std::string_view col_name) :
         column_(std::move(column)),
-        string_pool_(string_pool) {
+        string_pool_(string_pool),
+        column_name_(col_name) {
     }
 
     [[nodiscard]] std::optional<std::string_view> string_at_offset(entity::position_t offset, bool strip_fixed_width_trailing_nulls=false) const;
diff --git a/cpp/arcticdb/processing/operation_dispatch_binary.hpp b/cpp/arcticdb/processing/operation_dispatch_binary.hpp
index f29440949a..ee4693a6f7 100644
--- a/cpp/arcticdb/processing/operation_dispatch_binary.hpp
+++ b/cpp/arcticdb/processing/operation_dispatch_binary.hpp
@@ -44,6 +44,21 @@ VariantData binary_boolean(EmptyResult, EmptyResult, OperationType operation);
 // commutative, however if that were to change we would need the full set
 VariantData visit_binary_boolean(const VariantData& left, const VariantData& right, OperationType operation);
 
+template <typename Func>
+inline std::string binary_operation_column_name(std::string_view left_column, Func&& func, std::string_view right_column) {
+    return fmt::format("({} {} {})", left_column, func, right_column);
+}
+
+template <typename Func>
+inline std::string binary_operation_with_types_to_string(std::string_view left, const TypeDescriptor& type_left, Func&& func,
+                                                         std::string_view right, const TypeDescriptor& type_right,
+                                                         bool arguments_reversed = false) {
+    if (arguments_reversed) {
+        return fmt::format("{} ({}) {} {} ({})", right, get_user_friendly_type_string(type_right), func, left, get_user_friendly_type_string(type_left));
+    }
+    return fmt::format("{} ({}) {} {} ({})", left, get_user_friendly_type_string(type_left), func, right, get_user_friendly_type_string(type_right));
+}
+
 template <typename Func>
 VariantData binary_membership(const ColumnWithStrings& column_with_strings, ValueSet& value_set, Func&& func) {
     if (is_empty_type(column_with_strings.column_->type().data_type())) {
@@ -88,7 +103,7 @@ VariantData binary_membership(const ColumnWithStrings& column_with_strings, Valu
                     return func(offset, offset_set);
                 });
             } else if constexpr (is_bool_type(col_type_info::data_type) && is_bool_type(val_set_type_info::data_type)) {
-                util::raise_rte("Binary membership not implemented for bools");
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Binary membership '{}' not implemented for bools", func);
             } else if constexpr (is_numeric_type(col_type_info::data_type) && is_numeric_type(val_set_type_info::data_type)) {
                 using WideType = typename type_arithmetic_promoted_type<typename col_type_info::RawType,typename val_set_type_info::RawType, std::remove_reference_t<Func>>::type;
                 auto typed_value_set = value_set.get_set<WideType>();
@@ -105,8 +120,11 @@ VariantData binary_membership(const ColumnWithStrings& column_with_strings, Valu
                     }
                 });
             } else {
-                util::raise_rte("Cannot check membership of {} in set of {} (possible categorical?)",
-                                column_with_strings.column_->type(), value_set.base_type());
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Cannot check membership '{}' of {} {} in set of {}",
+                                func,
+                                column_with_strings.column_name_,
+                                get_user_friendly_type_string(column_with_strings.column_->type()),
+                                get_user_friendly_type_string(value_set.base_type()));
             }
         });
     });
@@ -126,7 +144,8 @@ VariantData visit_binary_membership(const VariantData &left, const VariantData &
             return transform_to_placeholder(binary_membership(l, *r, std::forward<decltype(func)>(func)));
             },
             [](const auto &, const auto&) -> VariantData {
-            util::raise_rte("Binary membership operations must be Column/ValueSet");
+            user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Binary membership operations must be Column/ValueSet");
+            return EmptyResult{};
         }
         }, left, right);
 }
@@ -170,7 +189,13 @@ VariantData binary_comparator(const ColumnWithStrings& left, const ColumnWithStr
                     return func(static_cast<typename comp::left_type>(left_value), static_cast<typename comp::right_type>(right_value));
                 });
             } else {
-                util::raise_rte("Cannot compare {} to {} (possible categorical?)", left.column_->type(), right.column_->type());
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Invalid comparison {}",
+                                binary_operation_with_types_to_string(
+                                        left.column_name_,
+                                        left.column_->type(),
+                                        func,
+                                        right.column_name_,
+                                        right.column_->type()));
             }
         });
     });
@@ -237,7 +262,14 @@ VariantData binary_comparator(const ColumnWithStrings& column_with_strings, cons
                 });
 
             } else {
-                util::raise_rte("Cannot compare {} to {} (possible categorical?)", column_with_strings.column_->type(), val.type());
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Invalid comparison {}",
+                                binary_operation_with_types_to_string(
+                                        column_with_strings.column_name_,
+                                        column_with_strings.column_->type(),
+                                        func,
+                                        val.to_string<typename val_type_info::RawType>(),
+                                        val.type(),
+                                        arguments_reversed));
             }
         });
     });
@@ -265,10 +297,12 @@ VariantData visit_binary_comparator(const VariantData& left, const VariantData&
         return transform_to_placeholder(result);
         },
         [&] ([[maybe_unused]] const std::shared_ptr<Value>& l, [[maybe_unused]] const std::shared_ptr<Value>& r) ->VariantData  {
-        util::raise_rte("Two value inputs not accepted to binary comparators");
+        user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Two value inputs not accepted to binary comparators");
+        return EmptyResult{};
         },
         [](const auto &, const auto&) -> VariantData {
-        util::raise_rte("Bitset/ValueSet inputs not accepted to binary comparators");
+        user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Bitset/ValueSet inputs not accepted to binary comparators");
+        return EmptyResult{};
     }
     }, left, right);
 }
@@ -279,16 +313,19 @@ VariantData binary_operator(const Value& left, const Value& right, Func&& func)
 
     details::visit_type(left.type().data_type(), [&](auto left_tag) {
         using left_type_info = ScalarTypeInfo<decltype(left_tag)>;
-        if constexpr(!is_numeric_type(left_type_info::data_type)) {
-            util::raise_rte("Non-numeric type provided to binary operation: {}", left.type());
-        }
-        auto left_value = *reinterpret_cast<const typename left_type_info::RawType*>(left.data_);
         details::visit_type(right.type().data_type(), [&](auto right_tag) {
             using right_type_info = ScalarTypeInfo<decltype(right_tag)>;
-            if constexpr(!is_numeric_type(right_type_info::data_type)) {
-                util::raise_rte("Non-numeric type provided to binary operation: {}", right.type());
+            if constexpr(!is_numeric_type(left_type_info::data_type) || !is_numeric_type(right_type_info::data_type)) {
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Non-numeric type provided to binary operation: {}",
+                                binary_operation_with_types_to_string(
+                                    left.to_string<typename left_type_info::RawType>(),
+                                    left.type(),
+                                    func,
+                                    right.to_string<typename right_type_info::RawType>(),
+                                    right.type()));
             }
             auto right_value = *reinterpret_cast<const typename right_type_info::RawType*>(right.data_);
+            auto left_value = *reinterpret_cast<const typename left_type_info::RawType*>(left.data_);
             using TargetType = typename type_arithmetic_promoted_type<typename left_type_info::RawType, typename right_type_info::RawType, std::remove_reference_t<Func>>::type;
             output_value->data_type_ = data_type_from_raw_type<TargetType>();
             *reinterpret_cast<TargetType*>(output_value->data_) = func.apply(left_value, right_value);
@@ -298,79 +335,90 @@ VariantData binary_operator(const Value& left, const Value& right, Func&& func)
 }
 
 template <typename Func>
-VariantData binary_operator(const Column& left, const Column& right, Func&& func) {
+VariantData binary_operator(const ColumnWithStrings& left, const ColumnWithStrings& right, Func&& func) {
     schema::check<ErrorCode::E_UNSUPPORTED_COLUMN_TYPE>(
-            !is_empty_type(left.type().data_type()) && !is_empty_type(right.type().data_type()),
+            !is_empty_type(left.column_->type().data_type()) && !is_empty_type(right.column_->type().data_type()),
             "Empty column provided to binary operator");
     std::unique_ptr<Column> output_column;
 
-    details::visit_type(left.type().data_type(), [&](auto left_tag) {
+    details::visit_type(left.column_->type().data_type(), [&](auto left_tag) {
         using left_type_info = ScalarTypeInfo<decltype(left_tag)>;
-        if constexpr(!is_numeric_type(left_type_info::data_type)) {
-            util::raise_rte("Non-numeric type provided to binary operation: {}", left.type());
-        }
-        details::visit_type(right.type().data_type(), [&](auto right_tag) {
+        details::visit_type(right.column_->type().data_type(), [&](auto right_tag) {
             using right_type_info = ScalarTypeInfo<decltype(right_tag)>;
-            if constexpr(!is_numeric_type(right_type_info::data_type)) {
-                util::raise_rte("Non-numeric type provided to binary operation: {}", right.type());
+            if constexpr(!is_numeric_type(left_type_info::data_type) || !is_numeric_type(right_type_info::data_type)) {
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Non-numeric column provided to binary operation: {}",
+                                binary_operation_with_types_to_string(
+                                        left.column_name_,
+                                        left.column_->type(),
+                                        func,
+                                        right.column_name_,
+                                        right.column_->type()));
             }
             using TargetType = typename type_arithmetic_promoted_type<typename left_type_info::RawType, typename right_type_info::RawType, std::remove_reference_t<decltype(func)>>::type;
             constexpr auto output_data_type = data_type_from_raw_type<TargetType>();
             output_column = std::make_unique<Column>(make_scalar_type(output_data_type), true);
             Column::transform<typename left_type_info::TDT, typename right_type_info::TDT, ScalarTagType<DataTypeTag<output_data_type>>>(
-                    left,
-                    right,
+                    *(left.column_),
+                    *(right.column_),
                     *output_column,
                     [&func] (auto left_value, auto right_value) -> TargetType {
                         return func.apply(left_value, right_value);
                     });
         });
     });
-    return VariantData(ColumnWithStrings(std::move(output_column)));
+    return VariantData(ColumnWithStrings(std::move(output_column), binary_operation_column_name(left.column_name_, func, right.column_name_)));
 }
 
 template <typename Func, bool arguments_reversed = false>
-VariantData binary_operator(const Column& col, const Value& val, Func&& func) {
+VariantData binary_operator(const ColumnWithStrings& col, const Value& val, Func&& func) {
     schema::check<ErrorCode::E_UNSUPPORTED_COLUMN_TYPE>(
-            !is_empty_type(col.type().data_type()),
+            !is_empty_type(col.column_->type().data_type()),
             "Empty column provided to binary operator");
     std::unique_ptr<Column> output_column;
+    std::string column_name;
 
-    details::visit_type(col.type().data_type(), [&](auto col_tag) {
+    details::visit_type(col.column_->type().data_type(), [&](auto col_tag) {
         using col_type_info = ScalarTypeInfo<decltype(col_tag)>;
-        if constexpr(!is_numeric_type(col_type_info::data_type)) {
-            util::raise_rte("Non-numeric type provided to binary operation: {}", col.type());
-        }
         details::visit_type(val.type().data_type(), [&](auto val_tag) {
             using val_type_info = ScalarTypeInfo<decltype(val_tag)>;
-            if constexpr(!is_numeric_type(val_type_info::data_type)) {
-                util::raise_rte("Non-numeric type provided to binary operation: {}", val.type());
+            if constexpr(!is_numeric_type(col_type_info::data_type) || !is_numeric_type(val_type_info::data_type)) {
+                std::string error_message;
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Non-numeric type provided to binary operation: {}",
+                                binary_operation_with_types_to_string(
+                                        col.column_name_,
+                                        col.column_->type(),
+                                        func,
+                                        val.to_string<typename val_type_info::RawType>(),
+                                        val.type(),
+                                        arguments_reversed));
             }
             auto raw_value = *reinterpret_cast<const typename val_type_info::RawType*>(val.data_);
             using TargetType = typename type_arithmetic_promoted_type<typename col_type_info::RawType, typename val_type_info::RawType, std::remove_reference_t<decltype(func)>>::type;
             using ReversedTargetType = typename type_arithmetic_promoted_type<typename val_type_info::RawType, typename col_type_info::RawType, std::remove_reference_t<decltype(func)>>::type;
             if constexpr(arguments_reversed) {
+                column_name = binary_operation_column_name(fmt::format("{}", raw_value), func, col.column_name_);
                 constexpr auto output_data_type = data_type_from_raw_type<ReversedTargetType>();
                 output_column = std::make_unique<Column>(make_scalar_type(output_data_type), true);
                 Column::transform<typename col_type_info::TDT, ScalarTagType<DataTypeTag<output_data_type>>>(
-                        col,
+                        *(col.column_),
                         *output_column,
-                        [&func, raw_value](auto input_value) -> ReversedTargetType {
-                    return func.apply(raw_value, input_value);
+                        [&func, &col, &column_name, raw_value](auto input_value) -> ReversedTargetType {
+                            return func.apply(raw_value, input_value);
                 });
             } else {
+                column_name = binary_operation_column_name(col.column_name_, func, fmt::format("{}", raw_value));
                 constexpr auto output_data_type = data_type_from_raw_type<TargetType>();
                 output_column = std::make_unique<Column>(make_scalar_type(output_data_type), true);
                 Column::transform<typename col_type_info::TDT, ScalarTagType<DataTypeTag<output_data_type>>>(
-                        col,
+                        *(col.column_),
                         *output_column,
-                        [&func, raw_value](auto input_value) -> TargetType {
-                    return func.apply(input_value, raw_value);
+                        [&func, &col, &column_name, raw_value](auto input_value) -> TargetType {
+                            return func.apply(input_value, raw_value);
                 });
             }
         });
     });
-    return {ColumnWithStrings(std::move(output_column))};
+    return {ColumnWithStrings(std::move(output_column), column_name)};
 }
 
 template<typename Func>
@@ -380,20 +428,21 @@ VariantData visit_binary_operator(const VariantData& left, const VariantData& ri
 
     return std::visit(util::overload {
         [&] (const ColumnWithStrings& l, const std::shared_ptr<Value>& r) ->VariantData  {
-            return binary_operator<decltype(func)>(*(l.column_), *r, std::forward<decltype(func)>(func));
+            return binary_operator<decltype(func)>(l, *r, std::forward<decltype(func)>(func));
             },
             [&] (const ColumnWithStrings& l, const ColumnWithStrings& r)  ->VariantData {
-            return binary_operator<decltype(func)>(*(l.column_), *(r.column_), std::forward<decltype(func)>(func));
+            return binary_operator<decltype(func)>(l, r, std::forward<decltype(func)>(func));
             },
             [&](const std::shared_ptr<Value>& l, const ColumnWithStrings& r) ->VariantData {
-            return binary_operator<decltype(func), true>(*(r.column_), *l, std::forward<decltype(func)>(func));
+            return binary_operator<decltype(func), true>(r, *l, std::forward<decltype(func)>(func));
             },
             [&] (const std::shared_ptr<Value>& l, const std::shared_ptr<Value>& r) -> VariantData {
             return binary_operator<decltype(func)>(*l, *r, std::forward<decltype(func)>(func));
             },
             [](const auto &, const auto&) -> VariantData {
-            util::raise_rte("Bitset/ValueSet inputs not accepted to binary operators");
-        }
+            user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Bitset/ValueSet inputs not accepted to binary operators");
+            return EmptyResult{};
+            }
         }, left, right);
 }
 
diff --git a/cpp/arcticdb/processing/operation_dispatch_unary.hpp b/cpp/arcticdb/processing/operation_dispatch_unary.hpp
index 0f1bc821b4..f3cacffa2e 100644
--- a/cpp/arcticdb/processing/operation_dispatch_unary.hpp
+++ b/cpp/arcticdb/processing/operation_dispatch_unary.hpp
@@ -36,6 +36,11 @@ VariantData unary_boolean(FullResult, OperationType operation);
 
 VariantData visit_unary_boolean(const VariantData& left, OperationType operation);
 
+template <typename Func>
+inline std::string unary_operation_to_string(Func&& func, std::string_view operand_str) {
+    return fmt::format("{}({})", func, operand_str);
+}
+
 template <typename Func>
 VariantData unary_operator(const Value& val, Func&& func) {
     auto output = std::make_unique<Value>();
@@ -44,7 +49,9 @@ VariantData unary_operator(const Value& val, Func&& func) {
     details::visit_type(val.type().data_type(), [&](auto val_tag) {
         using type_info = ScalarTypeInfo<decltype(val_tag)>;
         if constexpr (!is_numeric_type(type_info::data_type)) {
-            util::raise_rte("Cannot perform arithmetic on {}", val.type());
+            user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Cannot perform unary operation {} ({})",
+                                                                  unary_operation_to_string(func,val.to_string<typename type_info::RawType>()),
+                                                                  get_user_friendly_type_string(val.type()));
         }
         auto value = *reinterpret_cast<const typename type_info::RawType*>(val.data_);
         using TargetType = typename unary_arithmetic_promoted_type<typename type_info::RawType, std::remove_reference_t<Func>>::type;
@@ -56,36 +63,40 @@ VariantData unary_operator(const Value& val, Func&& func) {
 }
 
 template <typename Func>
-VariantData unary_operator(const Column& col, Func&& func) {
+VariantData unary_operator(const ColumnWithStrings& col, Func&& func) {
     schema::check<ErrorCode::E_UNSUPPORTED_COLUMN_TYPE>(
-            !is_empty_type(col.type().data_type()),
-            "Empty column provided to unary operator");
+            !is_empty_type(col.column_->type().data_type()),
+            "Empty column provided to unary operation {} ({})",
+            unary_operation_to_string(func, col.column_name_),
+            get_user_friendly_type_string(col.column_->type()));
     std::unique_ptr<Column> output_column;
 
-    details::visit_type(col.type().data_type(), [&](auto col_tag) {
+    details::visit_type(col.column_->type().data_type(), [&](auto col_tag) {
         using type_info = ScalarTypeInfo<decltype(col_tag)>;
         if constexpr (is_numeric_type(type_info::data_type)) {
             using TargetType = typename unary_arithmetic_promoted_type<typename type_info::RawType, std::remove_reference_t<Func>>::type;
             constexpr auto output_data_type = data_type_from_raw_type<TargetType>();
             output_column = std::make_unique<Column>(make_scalar_type(output_data_type), true);
-            Column::transform<typename type_info::TDT, ScalarTagType<DataTypeTag<output_data_type>>>(col,
+            Column::transform<typename type_info::TDT, ScalarTagType<DataTypeTag<output_data_type>>>(*(col.column_),
                                                                                                      *output_column,
                                                                                                      [&func](auto input_value) -> TargetType {
                                                                                                          return func.apply(
                                                                                                                  input_value);
                                                                                                      });
         } else {
-            util::raise_rte("Cannot perform arithmetic on {}", col.type());
+            user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Cannot perform unary operation {} ({})",
+                                                                  unary_operation_to_string(func, col.column_name_),
+                                                                  get_user_friendly_type_string(col.column_->type()));
         }
     });
-    return {ColumnWithStrings(std::move(output_column))};
+    return {ColumnWithStrings(std::move(output_column), unary_operation_to_string(func, col.column_name_))};
 }
 
 template<typename Func>
 VariantData visit_unary_operator(const VariantData& left, Func&& func) {
     return std::visit(util::overload{
         [&] (const ColumnWithStrings& l) -> VariantData {
-            return unary_operator(*(l.column_), std::forward<Func>(func));
+            return unary_operator(l, std::forward<Func>(func));
             },
         [&] (const std::shared_ptr<Value>& l) -> VariantData {
             return unary_operator(*l, std::forward<Func>(func));
@@ -94,18 +105,19 @@ VariantData visit_unary_operator(const VariantData& left, Func&& func) {
             return l;
         },
         [](const auto&) -> VariantData {
-            util::raise_rte("Bitset/ValueSet inputs not accepted to unary operators");
+            user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Bitset/ValueSet inputs not accepted to unary operators");
+            return EmptyResult{};
         }
     }, left);
 }
 
 template <typename Func>
-VariantData unary_comparator(const Column& col, Func&& func) {
-    if (is_empty_type(col.type().data_type()) || is_integer_type(col.type().data_type())) {
+VariantData unary_comparator(const ColumnWithStrings& col, Func&& func) {
+    if (is_empty_type(col.column_->type().data_type()) || is_integer_type(col.column_->type().data_type())) {
         if constexpr (std::is_same_v<std::remove_reference_t<Func>, IsNullOperator>) {
-            return is_empty_type(col.type().data_type()) ? VariantData(FullResult{}) : VariantData(EmptyResult{});
+            return is_empty_type(col.column_->type().data_type()) ? VariantData(FullResult{}) : VariantData(EmptyResult{});
         } else if constexpr (std::is_same_v<std::remove_reference_t<Func>, NotNullOperator>) {
-            return is_empty_type(col.type().data_type()) ? VariantData(EmptyResult{}) : VariantData(FullResult{});
+            return is_empty_type(col.column_->type().data_type()) ? VariantData(EmptyResult{}) : VariantData(FullResult{});
         } else {
             internal::raise<ErrorCode::E_ASSERTION_FAILURE>("Unexpected operator passed to unary_comparator");
         }
@@ -113,9 +125,9 @@ VariantData unary_comparator(const Column& col, Func&& func) {
 
     util::BitSet output_bitset;
     constexpr auto sparse_missing_value_output = std::is_same_v<std::remove_reference_t<Func>, IsNullOperator>;
-    details::visit_type(col.type().data_type(), [&](auto col_tag) {
+    details::visit_type(col.column_->type().data_type(), [&](auto col_tag) {
         using type_info = ScalarTypeInfo<decltype(col_tag)>;
-        Column::transform_to_bitset<typename type_info::TDT>(col, output_bitset, sparse_missing_value_output, [&func](auto input_value) -> bool {
+        Column::transform_to_bitset<typename type_info::TDT>(*(col.column_), output_bitset, sparse_missing_value_output, [&col, &func](auto input_value) -> bool {
             if constexpr (is_floating_point_type(type_info::data_type)) {
                 return func.apply(input_value);
             } else if constexpr (is_sequence_type(type_info::data_type)) {
@@ -123,7 +135,9 @@ VariantData unary_comparator(const Column& col, Func&& func) {
             } else if constexpr (is_time_type(type_info::data_type)) {
                 return func.template apply<TimeTypeTag>(input_value);
             } else {
-                internal::raise<ErrorCode::E_ASSERTION_FAILURE>("Cannot perform null checks on {}", type_info::data_type);
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Cannot perform null check: {} ({})",
+                                                                      unary_operation_to_string(func, col.column_name_),
+                                                                      get_user_friendly_type_string(col.column_->type()));
             }
         });
     });
@@ -134,7 +148,7 @@ template<typename Func>
 VariantData visit_unary_comparator(const VariantData& left, Func&& func) {
     return std::visit(util::overload{
             [&] (const ColumnWithStrings& l) -> VariantData {
-                return transform_to_placeholder(unary_comparator(*(l.column_), std::forward<decltype(func)>(func)));
+                return transform_to_placeholder(unary_comparator(l, std::forward<decltype(func)>(func)));
             },
             [] (EmptyResult) -> VariantData {
                 if constexpr (std::is_same_v<std::remove_reference_t<Func>, IsNullOperator>) {
@@ -146,7 +160,8 @@ VariantData visit_unary_comparator(const VariantData& left, Func&& func) {
                 }
             },
             [](const auto&) -> VariantData {
-                util::raise_rte("Bitset/ValueSet inputs not accepted to unary comparators");
+                user_input::raise<ErrorCode::E_INVALID_USER_ARGUMENT>("Bitset/ValueSet inputs not accepted to unary comparators");
+                return EmptyResult{};
             }
     }, left);
 }
diff --git a/cpp/arcticdb/processing/operation_types.hpp b/cpp/arcticdb/processing/operation_types.hpp
index 53d35b745f..27b30125aa 100644
--- a/cpp/arcticdb/processing/operation_types.hpp
+++ b/cpp/arcticdb/processing/operation_types.hpp
@@ -520,3 +520,182 @@ bool operator()(T t, const ankerl::unordered_dense::set<U>& u) const {
 };
 
 } //namespace arcticdb
+
+namespace fmt {
+template<>
+struct formatter<arcticdb::AbsOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::AbsOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "ABS");
+    }
+};
+
+template<>
+struct formatter<arcticdb::NegOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::NegOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "-");
+    }
+};
+
+template<>
+struct formatter<arcticdb::IsNullOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::IsNullOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "ISNULL");
+    }
+};
+
+template<>
+struct formatter<arcticdb::NotNullOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::NotNullOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "NOTNULL");
+    }
+};
+
+template<>
+struct formatter<arcticdb::PlusOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::PlusOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "+");
+    }
+};
+
+template<>
+struct formatter<arcticdb::MinusOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::MinusOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "-");
+    }
+};
+
+template<>
+struct formatter<arcticdb::TimesOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::TimesOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "*");
+    }
+};
+
+template<>
+struct formatter<arcticdb::DivideOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::DivideOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "/");
+    }
+};
+
+template<>
+struct formatter<arcticdb::EqualsOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::EqualsOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "==");
+    }
+};
+
+template<>
+struct formatter<arcticdb::NotEqualsOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::NotEqualsOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "!=");
+    }
+};
+
+template<>
+struct formatter<arcticdb::LessThanOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::LessThanOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "<");
+    }
+};
+
+template<>
+struct formatter<arcticdb::LessThanEqualsOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::LessThanEqualsOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "<=");
+    }
+};
+
+template<>
+struct formatter<arcticdb::GreaterThanOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::GreaterThanOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), ">");
+    }
+};
+
+template<>
+struct formatter<arcticdb::GreaterThanEqualsOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::GreaterThanEqualsOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), ">=");
+    }
+};
+
+template<>
+struct formatter<arcticdb::IsInOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::IsInOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "IS IN");
+    }
+};
+
+template<>
+struct formatter<arcticdb::IsNotInOperator> {
+    template<typename ParseContext>
+    constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
+
+    template<typename FormatContext>
+    constexpr auto format(const arcticdb::IsNotInOperator&, FormatContext &ctx) const {
+        return fmt::format_to(ctx.out(), "NOT IN");
+    }
+};
+
+} // namespace fmt
diff --git a/cpp/arcticdb/processing/processing_unit.cpp b/cpp/arcticdb/processing/processing_unit.cpp
index 2430022b6c..b221b3c069 100644
--- a/cpp/arcticdb/processing/processing_unit.cpp
+++ b/cpp/arcticdb/processing/processing_unit.cpp
@@ -53,7 +53,8 @@ VariantData ProcessingUnit::get(const VariantNode &name) {
                 return VariantData(ColumnWithStrings(
                         segment->column_ptr(
                         position_t(position_t(opt_idx.value()))),
-                        segment->string_pool_ptr()));
+                        segment->string_pool_ptr(),
+                        column_name.value));
             }
         }
         // Try multi-index column names
@@ -64,7 +65,8 @@ VariantData ProcessingUnit::get(const VariantNode &name) {
                 return VariantData(ColumnWithStrings(
                         segment->column_ptr(
                         position_t(*opt_idx)),
-                        segment->string_pool_ptr()));
+                        segment->string_pool_ptr(),
+                        column_name.value));
             }
         }
 


Test patch:
diff --git a/cpp/arcticdb/processing/test/benchmark_clause.cpp b/cpp/arcticdb/processing/test/benchmark_clause.cpp
index be8d570388..3d111660ff 100644
--- a/cpp/arcticdb/processing/test/benchmark_clause.cpp
+++ b/cpp/arcticdb/processing/test/benchmark_clause.cpp
@@ -116,7 +116,7 @@ void BM_hash_grouping_int(benchmark::State& state) {
     constexpr auto data_type = data_type_from_raw_type<integer>();
     Column column(make_scalar_type(data_type), num_rows, true, false);
     memcpy(column.ptr(), data.data(), num_rows * sizeof(integer));
-    ColumnWithStrings col_with_strings(std::move(column), {});
+    ColumnWithStrings col_with_strings(std::move(column), {}, "random_ints");
 
     grouping::HashingGroupers::Grouper<ScalarTagType<DataTypeTag<data_type>>> grouper;
     grouping::ModuloBucketizer bucketizer(num_buckets);
@@ -157,7 +157,7 @@ void BM_hash_grouping_string(benchmark::State& state) {
         column.set_scalar(idx, ofstr.offset());
     }
 
-    ColumnWithStrings col_with_strings(std::move(column), string_pool);
+    ColumnWithStrings col_with_strings(std::move(column), string_pool, "random_strings");
 
     grouping::HashingGroupers::Grouper<ScalarTagType<DataTypeTag<DataType::UTF_DYNAMIC64>>> grouper;
     grouping::ModuloBucketizer bucketizer(num_buckets);
diff --git a/cpp/arcticdb/processing/test/test_operation_dispatch.cpp b/cpp/arcticdb/processing/test/test_operation_dispatch.cpp
index 7f02eb27e5..fc4159929a 100644
--- a/cpp/arcticdb/processing/test/test_operation_dispatch.cpp
+++ b/cpp/arcticdb/processing/test/test_operation_dispatch.cpp
@@ -17,8 +17,8 @@
 TEST(OperationDispatch, unary_operator) {
     using namespace arcticdb;
     size_t num_rows = 100;
-    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)));
-    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()));
+    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)), "int_col");
+    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()), "empty_col");
 
     // int col
     auto variant_data = visit_unary_operator(int_column, NegOperator{});
@@ -34,8 +34,8 @@ TEST(OperationDispatch, unary_operator) {
 TEST(OperationDispatch, binary_operator) {
     using namespace arcticdb;
     size_t num_rows = 100;
-    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)));
-    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()));
+    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)), "int_col");
+    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()), "empty_col");
     auto value = std::make_shared<Value>(static_cast<int64_t>(50), DataType::INT64);
 
     // int col + int col
@@ -77,8 +77,8 @@ TEST(OperationDispatch, binary_operator) {
 TEST(OperationDispatch, binary_comparator) {
     using namespace arcticdb;
     size_t num_rows = 100;
-    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)));
-    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()));
+    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)), "int_col");
+    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()), "empty_col");
     auto value = std::make_shared<Value>(static_cast<int64_t>(50), DataType::INT64);
 
     // int col < int col
@@ -113,8 +113,8 @@ TEST(OperationDispatch, binary_comparator) {
 TEST(OperationDispatch, binary_membership) {
     using namespace arcticdb;
     size_t num_rows = 100;
-    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)));
-    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()));
+    auto int_column = ColumnWithStrings(std::make_unique<Column>(generate_int_column(num_rows)), "int_col");
+    auto empty_column = ColumnWithStrings(std::make_unique<Column>(generate_empty_column()), "empty_col");
     std::unordered_set<int64_t> raw_set{0, 23, 82, static_cast<int64_t>(num_rows) - 1, 1000000};
     auto value_set = std::make_shared<ValueSet>(std::make_shared<std::unordered_set<int64_t>>(raw_set));
 
diff --git a/python/tests/unit/arcticdb/version_store/test_filtering.py b/python/tests/unit/arcticdb/version_store/test_filtering.py
index 6a325b5917..2beb0afc78 100644
--- a/python/tests/unit/arcticdb/version_store/test_filtering.py
+++ b/python/tests/unit/arcticdb/version_store/test_filtering.py
@@ -211,7 +211,7 @@ def test_filter_categorical(request, lib_type):
     q = q[q.a == "hi"]
     symbol = "test_filter_categorical"
     lib.write(symbol, df)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lib.read(symbol, query_builder=q)
 
 
@@ -282,32 +282,32 @@ def test_filter_bool_nonbool_comparison(lmdb_version_store):
     # bool column to string column
     q = QueryBuilder()
     q = q[q["bool"] == q["string"]]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         lib.read(symbol, query_builder=q)
     # bool column to numeric column
     q = QueryBuilder()
     q = q[q["bool"] == q["numeric"]]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         lib.read(symbol, query_builder=q)
     # bool column to string value
     q = QueryBuilder()
     q = q[q["bool"] == "test"]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         lib.read(symbol, query_builder=q)
     # bool column to numeric value
     q = QueryBuilder()
     q = q[q["bool"] == 0]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         lib.read(symbol, query_builder=q)
     # string column to bool value
     q = QueryBuilder()
     q = q[q["string"] == True]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         lib.read(symbol, query_builder=q)
     # numeric column to bool value
     q = QueryBuilder()
     q = q[q["numeric"] == True]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         lib.read(symbol, query_builder=q)
 
 
@@ -740,6 +740,50 @@ def test_filter_datetime_timezone_aware_hypothesis(version_store_factory, df_dt,
             assert False
         assert True
 
+def test_df_query_wrong_type(lmdb_version_store_small_segment):
+    lib = lmdb_version_store_small_segment
+
+    df1 = pd.DataFrame({"col1": [1, 2, 3], "col2": [2, 3, 4], "col3": [4, 5, 6],
+                        "col_str": ["1", "2", "3"], "col_bool": [True, False, True]})
+    sym = "symbol"
+    lib.write(sym, df1)
+
+    str_vals = np.array(["2", "3", "4", "5"])
+    q = QueryBuilder()
+    q = q[q["col1"].isin(str_vals)]
+    with pytest.raises(UserInputException, match="Cannot check membership 'IS IN' of col1.*type=INT.*in set of.*type=STRING"):
+        lib.read(sym, query_builder=q)
+
+    q = QueryBuilder()
+    q = q[q["col_bool"].isnull()]
+    with pytest.raises(UserInputException, match="Cannot perform null check: ISNULL\(col_bool\).*type=BOOL"):
+        lib.read(sym, query_builder=q)
+
+    q = QueryBuilder()
+    q = q[q["col1"] / q["col_str"] == 3]
+    with pytest.raises(UserInputException, match="Non-numeric column provided to binary operation: col1.*type=INT.*/.*col_str.*type=STRING"):
+        lib.read(sym, query_builder=q)
+
+    q = QueryBuilder()
+    q = q[q["col1"] + "1" == 3]
+    with pytest.raises(UserInputException, match="Non-numeric type provided to binary operation: col1.*type=INT.*\+ \"1\".*type=STRING"):
+        lib.read(sym, query_builder=q)
+
+    q = QueryBuilder()
+    q = q[-q["col_str"] == 3]
+    with pytest.raises(UserInputException, match="Cannot perform unary operation -\(col_str\).*type=STRING"):
+        lib.read(sym, query_builder=q)
+
+    q = QueryBuilder()
+    q = q[q["col1"] - 1 >= "1"]
+    with pytest.raises(UserInputException, match="Invalid comparison.*col1 - 1.*type=INT.*>=.*\"1\".*type=STRING"):
+        lib.read(sym, query_builder=q)
+
+    q = QueryBuilder()
+    q = q[1 + q["col1"] * q["col2"] - q["col3"] == q["col_str"]]
+    # check that ((1 + (col1 * col2)) + col3) is generated as a column name and shown in the error message
+    with pytest.raises(UserInputException, match="Invalid comparison.*\(1 \+ \(col1 \* col2\)\) - col3.*type=INT.*==.*col_str .*type=STRING"):
+        lib.read(sym, query_builder=q)
 
 def test_filter_datetime_nanoseconds(lmdb_version_store):
     sym = "test_filter_datetime_nanoseconds"
@@ -790,11 +834,11 @@ def test_filter_compare_string_number_col_val(lmdb_version_store, df, val):
     q = q[q["a"] < val]
     symbol = "test_filter_compare_string_number_col_val"
     lmdb_version_store.write(symbol, df)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
-    q = q[val < q["a"]]
-    with pytest.raises(InternalException) as e_info:
+    q = q[(val < q["a"])]
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -807,11 +851,11 @@ def test_filter_compare_string_number_val_col(lmdb_version_store, df, val):
     q = q[val < q["a"]]
     symbol = "test_filter_compare_string_number_val_col"
     lmdb_version_store.write(symbol, df)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[q["a"] < val]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -828,11 +872,11 @@ def test_filter_compare_string_number_col_col(lmdb_version_store, df):
     q = q[q["a"] < q["b"]]
     symbol = "test_filter_compare_string_number_col_col"
     lmdb_version_store.write(symbol, df)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[q["b"] < q["a"]]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -849,7 +893,7 @@ def test_filter_isin_string_number_signed(lmdb_version_store, df, vals):
     q = q[q["a"].isin(vals)]
     symbol = "test_filter_isin_string_number"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -866,7 +910,7 @@ def test_filter_isin_string_number_unsigned(lmdb_version_store, df, vals):
     q = q[q["a"].isin(vals)]
     symbol = "test_filter_isin_string_number"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -882,7 +926,7 @@ def test_filter_isin_number_string(lmdb_version_store, df, vals):
     q = q[q["a"].isin(vals)]
     symbol = "test_filter_isin_number_string"
     lmdb_version_store.write(symbol, df)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1395,11 +1439,11 @@ def test_filter_arithmetic_string_number_col_val(lmdb_version_store, df, val):
     q = q[(q["a"] + val) < 10]
     symbol = "test_filter_arithmetic_string_number_col_val"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[(val + q["a"]) < 10]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1412,11 +1456,11 @@ def test_filter_arithmetic_string_number_val_col(lmdb_version_store, df, val):
     q = q[(q["a"] + val) < 10]
     symbol = "test_filter_arithmetic_string_number_val_col"
     lmdb_version_store.write(symbol, df)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[(val + q["a"]) < 10]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1433,11 +1477,11 @@ def test_filter_arithmetic_string_number_col_col(lmdb_version_store, df):
     q = q[(q["a"] + q["b"]) < 10]
     symbol = "test_filter_arithmetic_string_number_val_col"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[(q["b"] + q["a"]) < 10]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1450,11 +1494,11 @@ def test_filter_arithmetic_string_string_col_val(lmdb_version_store, df, val):
     q = q[(q["a"] + val) < 10]
     symbol = "test_filter_arithmetic_string_string_col_val"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[(val + q["a"]) < 10]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1467,11 +1511,11 @@ def test_filter_arithmetic_string_string_val_col(lmdb_version_store, df, val):
     q = q[(q["a"] + val) < 10]
     symbol = "test_filter_arithmetic_string_string_val_col"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[(val + q["a"]) < 10]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1488,11 +1532,11 @@ def test_filter_arithmetic_string_string_col_col(lmdb_version_store, df):
     q = q[(q["a"] + q["b"]) < 10]
     symbol = "test_filter_arithmetic_string_string_col_col"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
     q = QueryBuilder()
     q = q[(q["b"] + q["a"]) < 10]
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1519,7 +1563,7 @@ def test_filter_abs_string(lmdb_version_store, df, val):
     q = q[abs(q["a"]) < val]
     symbol = "test_filter_abs_string"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)
 
 
@@ -1546,7 +1590,7 @@ def test_filter_neg_string(lmdb_version_store, df, val):
     q = q[-q["a"] < val]
     symbol = "test_filter_neg_string"
     lmdb_version_store.write(symbol, df, dynamic_strings=True)
-    with pytest.raises(InternalException) as e_info:
+    with pytest.raises(UserInputException) as e_info:
         _ = lmdb_version_store.read(symbol, query_builder=q)