{"record":{"id":"2257417014b8c2b7","repo":"deepset-ai/haystack","slug":"route-must-contain-condition-output-output","errorCode":null,"errorMessage":"Route must contain 'condition', 'output', 'output_type' and 'output_name' fields: {route}","messagePattern":"Route must contain 'condition', 'output', 'output_type' and 'output_name' fields: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/components/routers/conditional_router.py","lineNumber":497,"sourceCode":"\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\n        :param routes: A list of routes.\n        \"\"\"\n        for route in routes:\n            try:\n                keys = set(route.keys())\n            except AttributeError as e:\n                raise ValueError(f\"Route must be a dictionary, got: {route}\") from e\n\n            mandatory_fields = {\"condition\", \"output\", \"output_type\", \"output_name\"}\n            has_all_mandatory_fields = mandatory_fields.issubset(keys)\n            if not has_all_mandatory_fields:\n                raise ValueError(\n                    f\"Route must contain 'condition', 'output', 'output_type' and 'output_name' fields: {route}\"\n                )\n\n            # Validate outputs are consistent\n            outputs = route[\"output\"] if isinstance(route[\"output\"], list) else [route[\"output\"]]\n            output_types = route[\"output_type\"] if isinstance(route[\"output_type\"], list) else [route[\"output_type\"]]\n            output_names = route[\"output_name\"] if isinstance(route[\"output_name\"], list) else [route[\"output_name\"]]\n\n            # Check lengths match\n            if not len(outputs) == len(output_types) == len(output_names):\n                raise ValueError(f\"Route output, output_type and output_name must have same length: {route}\")\n\n            # Condition is always a Jinja2 template — validate it\n            if not self._validate_template(self._env, route[\"condition\"]):\n                condition_value = route[\"condition\"]\n                if not isinstance(condition_value, str):\n                    raise ValueError(\n                        f\"Invalid template for condition: {condition_value!r} (type: {type(condition_value).__name__}).\"","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/routers/conditional_router.py#L479-L515","documentation":"Each route dict must define all four mandatory fields: condition, output, output_type, and output_name. _validate_routes (called from __init__) raises ValueError when any of these keys is missing from a route.","triggerScenarios":"ConditionalRouter(routes=[...]) with a route dict missing one or more of condition/output/output_type/output_name.","commonSituations":"Copying example routes and deleting fields; hand-written YAML omitting output_name or output_type; refactors that rename keys without updating all routes.","solutions":["Add the missing field(s) named in the mandatory set to each route dict.","Validate your route dicts against the required keys before constructing the router.","If migrating from older configs, add output_name/output_type which newer versions require."],"exampleFix":"// before\n{\"condition\": \"x > 1\", \"output\": \"out\"}\n// after\n{\"condition\": \"x > 1\", \"output\": \"out\", \"output_type\": str, \"output_name\": \"out\"}","handlingStrategy":"validation","validationCode":"REQUIRED = {\"condition\", \"output\", \"output_type\", \"output_name\"}\nfor i, r in enumerate(routes):\n    missing = REQUIRED - set(r.keys())\n    assert not missing, f\"route {i} missing fields: {missing}\"","typeGuard":"from typing import TypedDict\nclass Route(TypedDict):\n    condition: str\n    output: str | list\n    output_type: type | list\n    output_name: str | list\n\ndef routes_conform(routes) -> bool:\n    return all(set(r.keys()) >= {\"condition\", \"output\", \"output_type\", \"output_name\"} for r in routes)","tryCatchPattern":"try:\n    router = ConditionalRouter(routes=routes)\nexcept ValueError as e:\n    if \"must contain 'condition'\" in str(e):\n        routes = [fill_defaults(r) for r in routes]\n        router = ConditionalRouter(routes=routes)\n    else:\n        raise","preventionTips":["Use the Route TypedDict/dataclass so type checkers enforce the four mandatory fields.","Keep a canonical route template and copy it for each new route.","Validate route YAML against a JSON schema at load time."],"tags":["router","validation","missing-field"],"backgroundTag":"invalid-route-config","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}