diff --git a/haystack/components/routers/conditional_router.py b/haystack/components/routers/conditional_router.py index 7b310d0419..6a47dfa781 100644 --- a/haystack/components/routers/conditional_router.py +++ b/haystack/components/routers/conditional_router.py @@ -398,10 +398,23 @@ def _validate_routes(self, routes: list[Route]): # Validate templates if not self._validate_template(self._env, route["condition"]): - raise ValueError(f"Invalid template for condition: {route['condition']}") + condition_value = route["condition"] + if not isinstance(condition_value, str): + raise ValueError( + f"Invalid template for condition: {condition_value!r} (type: {type(condition_value).__name__})." + f"Condition must be a string representing a valid Jinja2 template. " + f"For example, use {str(condition_value)!r} instead of {condition_value!r}." + ) + raise ValueError(f"Invalid template for condition: {condition_value}") for output in outputs: if not self._validate_template(self._env, output): + if not isinstance(output, str): + raise ValueError( + f"Invalid template for output: {output!r} (type: {type(output).__name__}). " + f"Output must be a string representing a valid Jinja2 template. " + f"For example, use {str(output)!r} instead of {output!r}." + ) raise ValueError(f"Invalid template for output: {output}") @staticmethod @@ -429,6 +442,9 @@ def _validate_template(self, env: Environment, template_text: str): :param template_text: A Jinja template string. :returns: `True` if the template is valid, `False` otherwise. """ + # Check if template_text is a string before attempting to parse + if not isinstance(template_text, str): + return False try: env.parse(template_text) return True diff --git a/releasenotes/notes/conditional-router-template-error-message-f633f9e786a8c7fc.yaml b/releasenotes/notes/conditional-router-template-error-message-f633f9e786a8c7fc.yaml new file mode 100644 index 0000000000..8934e1c0dc --- /dev/null +++ b/releasenotes/notes/conditional-router-template-error-message-f633f9e786a8c7fc.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Improved error messages in ConditionalRouter when non-string values are provided as route outputs. + Users now receive clear guidance (e.g., "use '2' instead of 2") instead of the cryptic "Can't compile non template nodes" error. diff --git a/test/components/routers/test_conditional_router.py b/test/components/routers/test_conditional_router.py index 17a933d845..1dd26f3289 100644 --- a/test/components/routers/test_conditional_router.py +++ b/test/components/routers/test_conditional_router.py @@ -40,6 +40,47 @@ def test_invalid_condition_field(self): with pytest.raises(ValueError, match="Invalid template"): ConditionalRouter(routes) + def test_invalid_output_template_non_string(self): + """ + ConditionalRouter init raises a ValueError with helpful error message when output is not a string + """ + # output is an int instead of a string template + routes = [ + { + "condition": '{{ flag == "double" }}', + "output": 2, + "output_name": "num_additional_outputs", + "output_type": int, + } + ] + with pytest.raises(ValueError) as exc_info: + ConditionalRouter(routes) + error_message = str(exc_info.value) + assert "Invalid template for output" in error_message + assert "string" in error_message + assert "Jinja2 template" in error_message + assert "2" in error_message + + def test_invalid_output_template_non_string_list(self): + """ + ConditionalRouter init raises a ValueError with helpful error message when output in list is not a string + """ + # output list contains an int instead of a string template + routes = [ + { + "condition": '{{ flag == "double" }}', + "output": ["{{streams}}", 2], + "output_name": ["streams", "num"], + "output_type": [list[int], int], + } + ] + with pytest.raises(ValueError) as exc_info: + ConditionalRouter(routes) + error_message = str(exc_info.value) + assert "Invalid template for output" in error_message + assert "string" in error_message + assert "Jinja2 template" in error_message + def test_no_vars_in_output_route_but_with_output_name(self): """ Router can't accept a route with no variables used in the output field