@@ -70,6 +70,27 @@ class RemoteOnlineStore(OnlineStore):
7070 remote online store implementation wrapper to communicate with feast online server.
7171 """
7272
73+ @staticmethod
74+ def _proto_value_to_transport_value (proto_value : ValueProto ) -> Any :
75+ """
76+ Convert a proto Value to a JSON-serializable Python value suitable for
77+ HTTP transport. Unlike ``feast_value_type_to_python_type``, this keeps
78+ ``json_val`` as a raw string so the receiving server can reconstruct a
79+ DataFrame whose column types match the original (string for JSON, dict
80+ for Map/Struct). Parsing JSON strings into dicts would cause PyArrow to
81+ infer a struct column on the server, which can crash with complex nested
82+ types (lists inside dicts).
83+ """
84+ val_attr = proto_value .WhichOneof ("val" )
85+ if val_attr is None :
86+ return None
87+
88+ # Keep JSON values as raw strings for correct DataFrame reconstruction
89+ if val_attr == "json_val" :
90+ return getattr (proto_value , val_attr )
91+
92+ return feast_value_type_to_python_type (proto_value )
93+
7394 def online_write_batch (
7495 self ,
7596 config : RepoConfig ,
@@ -97,10 +118,11 @@ def online_write_batch(
97118 feast_value_type_to_python_type (entity_value_proto )
98119 )
99120
100- # Populate feature values
121+ # Populate feature values – use transport-safe conversion that
122+ # preserves JSON strings instead of parsing them into dicts.
101123 for feature_name , feature_value_proto in feature_values_proto .items ():
102124 columnar_data [feature_name ].append (
103- feast_value_type_to_python_type (feature_value_proto )
125+ self . _proto_value_to_transport_value (feature_value_proto )
104126 )
105127
106128 # Populate timestamps
0 commit comments