{"record":{"id":"5cca157777cd53ee","repo":"deepset-ai/haystack","slug":"route-output-name-type-doesn-t-match-expected","errorCode":null,"errorMessage":"Route '{output_name}' type doesn't match expected type","messagePattern":"Route '(.+?)' type doesn't match expected type","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/components/routers/conditional_router.py","lineNumber":467,"sourceCode":"                                f\"Ensure '{output}' is passed as an input to the router.\"\n                            )\n                        output_value = kwargs[output]\n                    else:\n                        # Standard Jinja2 template evaluation\n                        t_output = self._env.from_string(output)\n                        output_value = t_output.render(**kwargs)\n\n                        # We suppress the exception in case the output is already a string, otherwise\n                        # we try to evaluate it and would fail.\n                        # This must be done cause the output could be different literal structures.\n                        # This doesn't support any user types.\n                        with contextlib.suppress(Exception):\n                            if not self._unsafe:\n                                output_value = ast.literal_eval(output_value)\n\n                    # Validate output type if needed\n                    if self._validate_output_type and not self._output_matches_type(output_value, output_type):\n                        raise ValueError(f\"Route '{output_name}' type doesn't match expected type\")  # noqa: TRY301\n\n                    result[output_name] = output_value\n\n                return result\n\n            except Exception as e:\n                # If this was a type-validation failure or missing passthrough variable, let it propagate\n                if isinstance(e, ValueError):\n                    raise\n                msg = f\"Error evaluating condition for route '{route}': {e}\"\n                raise RouteConditionException(msg) from e\n\n        raise NoRouteSelectedException(f\"No route fired. Routes: {self.routes}\")\n\n    def _validate_routes(self, routes: list[Route]) -> None:\n        \"\"\"\n        Validates a list of routes.\n","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/routers/conditional_router.py#L449-L485","documentation":"validate_output_type=True is enabled and the produced output_value of the fired route does not match the route's declared output_type. Haystack raises ValueError so misconfigured route typing fails loudly instead of passing wrongly-typed data downstream.","triggerScenarios":"ConditionalRouter(validate_output_type=True).run() where the fired route's rendered/evaluated output fails _output_matches_type against output_type (e.g. output is a string but output_type is int, or a Jinja-rendered output isn't literal_eval-able into the declared type).","commonSituations":"Jinja renders numbers as strings and literal_eval can't coerce them (in safe mode); output_type edited after changing the template; passthrough of an object whose runtime type differs from the declared one; invalid output_type annotations that make the matcher always fail.","solutions":["Correct the route's output_type to match what the template/variable actually produces.","Fix the output template so it renders a literal matching output_type (safe mode relies on ast.literal_eval).","Set validate_output_type=False if validation is not needed.","If the mismatch comes from a custom/invalid type, use a type _output_matches_type can handle."],"exampleFix":"// before\nRoute(condition=\"query.len > 10\", output=\"{{ query }}\", output_type=int, output_name=\"out\")\n// after\nRoute(condition=\"query.len > 10\", output=\"{{ query }}\", output_type=str, output_name=\"out\")","handlingStrategy":"validation","validationCode":"route = {\"condition\": \"q\", \"output\": \"{{ q }}\", \"output_type\": str, \"output_name\": \"out\"}\nrendered = ast.literal_eval(jinja_env.from_string(route[\"output\"]).render(q=\"x\"))\nassert isinstance(rendered, route[\"output_type\"]), \"declared output_type mismatch\"","typeGuard":"def output_matches(value, expected_type) -> bool:\n    import typing\n    return isinstance(value, expected_type) or typing.get_origin(expected_type) is not None and isinstance(value, typing.get_origin(expected_type))","tryCatchPattern":"try:\n    result = router.run(query=q)\nexcept ValueError as e:\n    if \"type doesn't match expected type\" in str(e):\n        result = fallback_router.run(query=q)\n    else:\n        raise","preventionTips":["Only enable validate_output_type when route output types are stable and known.","Remember safe-mode Jinja renders strings; rely on literal_eval-able output or passthrough for typed values.","Test each route's rendered output against its output_type in CI."],"tags":["router","type-validation","jinja"],"backgroundTag":"output-type-mismatch","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}