deepset-ai/haystack · error · NoRouteSelectedException

No route fired. Routes: {self.routes}

Error message

No route fired. Routes: {self.routes}

What it means

run() evaluated every route's condition and none rendered truthy, so no output could be selected. Haystack raises NoRouteSelectedException listing all configured routes to help debug why each condition was False.

Source

Thrown at haystack/components/routers/conditional_router.py:480

                            if not self._unsafe:
                                output_value = ast.literal_eval(output_value)

                    # Validate output type if needed
                    if self._validate_output_type and not self._output_matches_type(output_value, output_type):
                        raise ValueError(f"Route '{output_name}' type doesn't match expected type")  # noqa: TRY301

                    result[output_name] = output_value

                return result

            except Exception as e:
                # If this was a type-validation failure or missing passthrough variable, let it propagate
                if isinstance(e, ValueError):
                    raise
                msg = f"Error evaluating condition for route '{route}': {e}"
                raise RouteConditionException(msg) from e

        raise NoRouteSelectedException(f"No route fired. Routes: {self.routes}")

    def _validate_routes(self, routes: list[Route]) -> None:
        """
        Validates a list of routes.

        :param routes: A list of routes.
        """
        for route in routes:
            try:
                keys = set(route.keys())
            except AttributeError as e:
                raise ValueError(f"Route must be a dictionary, got: {route}") from e

            mandatory_fields = {"condition", "output", "output_type", "output_name"}
            has_all_mandatory_fields = mandatory_fields.issubset(keys)
            if not has_all_mandatory_fields:
                raise ValueError(
                    f"Route must contain 'condition', 'output', 'output_type' and 'output_name' fields: {route}"

View on GitHub (pinned to e318778c9b)

Solutions

  1. Add a final catch-all route with condition "True" as a default.
  2. Print/log the inputs passed to run() and test each condition template against them.
  3. Ensure conditions render to a Python-literal boolean in safe mode (e.g. "{{ query|length > 10 }}").
  4. Catch NoRouteSelectedException and implement fallback logic at the call site.

Example fix

// before
routes=[Route(condition="q == 'a'", output="a", output_type=str, output_name="out")]
// after
routes=[Route(condition="q == 'a'", output="a", output_type=str, output_name="out"),
        Route(condition="True", output="default", output_type=str, output_name="default")]
Defensive patterns

Strategy: try-catch

Validate before calling

fired = any(eval_literal(env.from_string(r["condition"]).render(**kwargs))
            for r in router.routes)
assert fired, "no route condition is True for given inputs"

Type guard

def some_route_fires(router, kwargs: dict) -> bool:
    import ast
    for r in router.routes:
        try:
            if ast.literal_eval(router._env.from_string(r["condition"]).render(**kwargs)):
                return True
        except Exception:
            continue
    return False

Try / catch

try:
    result = router.run(**kwargs)
except NoRouteSelectedException:
    result = {"default": fallback_value}

Prevention

When it happens

Trigger: ConditionalRouter.run() where every route's condition renders to False (or fails ast.literal_eval into a falsy value in safe mode) for the given inputs.

Common situations: Conditions that don't cover all input cases (no catch-all else route); variables rendered as strings ("True"/"False" handled, but comparisons like "1 == 2" fine while free text literal_eval fails to False-ish values); upstream component producing unexpected values.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/9e02c200420109e4. Report an issue: GitHub.