reflex-dev/reflex · error · MissingAnnotationError

MissingAnnotationError

Error message

MissingAnnotationError

What it means

Args specs for event triggers map incoming event arguments to names; when resolving annotations, a non-lambda callable's parameter has no annotation available, so the framework cannot type the event arg and raises MissingAnnotationError.

Source

Thrown at packages/reflex-base/src/reflex_base/event/__init__.py:2164

def resolve_annotation(annotations: dict[str, Any], arg_name: str, spec: ArgsSpec):
    """Resolve the annotation for the given argument name.

    Args:
        annotations: The annotations.
        arg_name: The argument name.
        spec: The specs which the annotations come from.

    Returns:
        The resolved annotation.

    Raises:
        MissingAnnotationError: If the annotation is missing for non-lambda methods.
    """
    annotation = annotations.get(arg_name)
    if annotation is None:
        if not isinstance(spec, types.LambdaType):
            raise MissingAnnotationError(var_name=arg_name)
        return dict[str, dict]
    return annotation


@lru_cache
def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
    """Parse the args provided in the ArgsSpec of an event trigger.

    Args:
        arg_spec: The spec of the args.

    Returns:
        The parsed args.
    """
    # if there's multiple, the first is the default
    if isinstance(arg_spec, Sequence):
        annotations = [get_type_hints(one_arg_spec) for one_arg_spec in arg_spec]
        arg_spec = arg_spec[0]

View on GitHub (pinned to 45b8ed5ab7)

Solutions

  1. Annotate all parameters of the function: def handle(e: rx.event.MouseEvent): ...
  2. Convert the function to a lambda if the arg types can be inferred from context
  3. Prefer decorated state event handlers (@rx.event) which enforce annotations

Example fix

# before
def do_thing(value):
    ...
rx.button('Go', on_click=lambda e: do_thing(e.value))
# after
def do_thing(value: str):
    ...
rx.button('Go', on_click=lambda e: do_thing(e.value))
Defensive patterns

Strategy: type-guard

Validate before calling

import typing, types

def fully_annotated(fn) -> bool:
    hints = typing.get_type_hints(fn)
    params = [p for p in typing.get_type_hints(fn, include_extras=False)]
    return all(p in hints for p in inspect.signature(fn).parameters)

Type guard

def has_all_annotations(fn) -> TypeGuard[Callable]: import inspect, typing; return all(p.annotation is not inspect.Parameter.empty for p in inspect.signature(fn).parameters.values())

Try / catch

try:
    rx.el.button('x', on_click=my_func)
except MissingAnnotationError:
    # add annotations to my_func params, or wrap in a lambda

Prevention

When it happens

Trigger: Passing a plain function (not a lambda) as an event trigger or arg spec whose parameters lack type annotations, e.g. on_click=plain_func where def plain_func(e): ... has no annotation for e.

Common situations: Using module-level utility functions instead of state handlers/lambdas; refactoring lambdas into named functions and dropping annotations.

Related errors


AI-assisted analysis of reflex-dev/reflex@45b8ed5ab7 (2026-08-28). Data as JSON: /api/errors/5dc9fcd1787bf9eb. Report an issue: GitHub.