@@ -81,7 +81,7 @@ async def get(self, node_id):
8181 return self .is_changed [node_id ]
8282
8383 # Intentionally do not use cached outputs here. We only want constants in IS_CHANGED
84- input_data_all , _ , v3_data = get_input_data (node ["inputs" ], class_def , node_id , None )
84+ input_data_all , _ , v3_data , _ = get_input_data (node ["inputs" ], class_def , node_id , None )
8585 try :
8686 is_changed = await _async_map_node_over_list (self .prompt_id , node_id , class_def , input_data_all , is_changed_name , v3_data = v3_data )
8787 is_changed = await resolve_map_node_over_list_results (is_changed )
@@ -213,7 +213,35 @@ def mark_missing():
213213 if h [x ] == "API_KEY_COMFY_ORG" :
214214 input_data_all [x ] = [extra_data .get ("api_key_comfy_org" , None )]
215215 v3_data ["hidden_inputs" ] = hidden_inputs_v3
216- return input_data_all , missing_keys , v3_data
216+ return input_data_all , missing_keys , v3_data , valid_inputs
217+
218+ def validate_resolved_inputs (input_data_all , class_def , valid_inputs ):
219+ """Validate resolved input values against schema constraints.
220+
221+ This is needed because validate_inputs() only sees direct widget values.
222+ Linked inputs aren't resolved during validate_inputs(), so this runs after resolution to catch any violations.
223+ """
224+ for x , values in input_data_all .items ():
225+ input_type , input_category , extra_info = get_input_info (class_def , x , valid_inputs )
226+ if input_type != "STRING" :
227+ continue
228+ min_length = extra_info .get ("minLength" )
229+ max_length = extra_info .get ("maxLength" )
230+ if min_length is None and max_length is None :
231+ continue
232+ for val in values :
233+ if val is None or not isinstance (val , str ):
234+ continue
235+ if min_length is not None and len (val ) < min_length :
236+ raise ValueError (
237+ f"Input '{ x } ': value length { len (val )} is shorter than "
238+ f"minimum length of { min_length } "
239+ )
240+ if max_length is not None and len (val ) > max_length :
241+ raise ValueError (
242+ f"Input '{ x } ': value length { len (val )} is longer than "
243+ f"maximum length of { max_length } "
244+ )
217245
218246map_node_over_list = None #Don't hook this please
219247
@@ -469,7 +497,7 @@ async def execute(server, dynprompt, caches, current_item, extra_data, executed,
469497 has_subgraph = False
470498 else :
471499 get_progress_state ().start_progress (unique_id )
472- input_data_all , missing_keys , v3_data = get_input_data (inputs , class_def , unique_id , execution_list , dynprompt , extra_data )
500+ input_data_all , missing_keys , v3_data , valid_inputs = get_input_data (inputs , class_def , unique_id , execution_list , dynprompt , extra_data )
473501 if server .client_id is not None :
474502 server .last_node_id = display_node_id
475503 server .send_sync ("executing" , { "node" : unique_id , "display_node" : display_node_id , "prompt_id" : prompt_id }, server .client_id )
@@ -498,6 +526,8 @@ async def execute(server, dynprompt, caches, current_item, extra_data, executed,
498526 execution_list .make_input_strong_link (unique_id , i )
499527 return (ExecutionResult .PENDING , None , None )
500528
529+ validate_resolved_inputs (input_data_all , class_def , valid_inputs )
530+
501531 def execution_block_cb (block ):
502532 if block .message is not None :
503533 mes = {
@@ -940,6 +970,34 @@ async def validate_inputs(prompt_id, prompt, item, validated):
940970 errors .append (error )
941971 continue
942972
973+ if input_type == "STRING" :
974+ if "minLength" in extra_info and len (val ) < extra_info ["minLength" ]:
975+ error = {
976+ "type" : "value_shorter_than_min_length" ,
977+ "message" : "Value length {} shorter than min length of {}" .format (len (val ), extra_info ["minLength" ]),
978+ "details" : f"{ x } " ,
979+ "extra_info" : {
980+ "input_name" : x ,
981+ "input_config" : info ,
982+ "received_value" : val ,
983+ }
984+ }
985+ errors .append (error )
986+ continue
987+ if "maxLength" in extra_info and len (val ) > extra_info ["maxLength" ]:
988+ error = {
989+ "type" : "value_longer_than_max_length" ,
990+ "message" : "Value length {} longer than max length of {}" .format (len (val ), extra_info ["maxLength" ]),
991+ "details" : f"{ x } " ,
992+ "extra_info" : {
993+ "input_name" : x ,
994+ "input_config" : info ,
995+ "received_value" : val ,
996+ }
997+ }
998+ errors .append (error )
999+ continue
1000+
9431001 if isinstance (input_type , list ) or input_type == io .Combo .io_type :
9441002 if input_type == io .Combo .io_type :
9451003 combo_options = extra_info .get ("options" , [])
@@ -971,7 +1029,7 @@ async def validate_inputs(prompt_id, prompt, item, validated):
9711029 continue
9721030
9731031 if len (validate_function_inputs ) > 0 or validate_has_kwargs :
974- input_data_all , _ , v3_data = get_input_data (inputs , obj_class , unique_id )
1032+ input_data_all , _ , v3_data , _ = get_input_data (inputs , obj_class , unique_id )
9751033 input_filtered = {}
9761034 for x in input_data_all :
9771035 if x in validate_function_inputs or validate_has_kwargs :
0 commit comments