reflex-dev/reflex · error · EventHandlerValueError

Form field mismatch for on_submit handler `{handler_name}`.\

Error message

Form field mismatch for on_submit handler `{handler_name}`.\n\nThe handler expects form data matching `{typed_dict_type.__name__}` with required fields:\n{_format_field_list(required_field_names)}\n\nFields missing from the form:\n{_format_field_list(missing_fields)}\n\nMatching fields present in the form:\n{_format_field_list(present_fields)}\n\nHint: Add controls with matching static `name` or `id` values, or make the TypedDict fields optional.

What it means

rx.el.form.validate_on_submit type-checks that every required field of the handler's TypedDict payload has a matching form control (by static name or id). Missing fields mean the submitted event data cannot satisfy the TypedDict at runtime.

Source

Thrown at packages/reflex-components-core/src/reflex_components_core/el/elements/forms.py:491

            if not missing_fields or has_dynamic_identifiers:
                continue

            present_fields = tuple(
                field for field in required_field_names if field in form_keys
            )
            msg = (
                f"Form field mismatch for on_submit handler `{handler_name}`.\n\n"
                f"The handler expects form data matching `{typed_dict_type.__name__}` "
                "with required fields:\n"
                f"{_format_field_list(required_field_names)}\n\n"
                "Fields missing from the form:\n"
                f"{_format_field_list(missing_fields)}\n\n"
                "Matching fields present in the form:\n"
                f"{_format_field_list(present_fields)}\n\n"
                "Hint: Add controls with matching static `name` or `id` values, or "
                "make the TypedDict fields optional."
            )
            raise EventHandlerValueError(msg)

    def _get_vars(
        self, include_children: bool = True, ignore_ids: set[int] | None = None
    ) -> Iterator[Var]:
        yield from super()._get_vars(
            include_children=include_children, ignore_ids=ignore_ids
        )
        yield from self._get_form_refs().values()

    def _exclude_props(self) -> list[str]:
        return [
            *super()._exclude_props(),
            "reset_on_submit",
            "handle_submit_unique_name",
        ]


HTMLInputTypeAttribute = Literal[

View on GitHub (pinned to 45b8ed5ab7)

Solutions

  1. Add form controls whose static name or id matches each required TypedDict field
  2. Use static string names: rx.input(name="email") instead of a Var-driven name
  3. Make genuinely optional TypedDict fields NotRequired
  4. Wrap dynamically-built forms in rx.el.form without validate_on_submit, or restructure so names are static

Example fix

# before
class FormData(TypedDict):
    email: str  # form has rx.input(placeholder="email") only
rx.el.form(..., validate_on_submit=True, on_submit=State.handle)
# after
rx.el.form(rx.input(name="email"), validate_on_submit=True, on_submit=State.handle)
Defensive patterns

Strategy: validation

Validate before calling

required = {k for k, t in FormData.__annotations__.items() if k not in getattr(FormData, '__optional_keys__', set())}
form_names = {'email', 'password'}  # names you actually render
missing = required - form_names
assert not missing, f'missing form controls: {missing}'

Type guard

def typeddict_required_fields(td: type[TypedDict]) -> set[str]:
    return set(td.__annotations__) - set(getattr(td, '__optional_keys__', set()))

Prevention

When it happens

Trigger: Using on_submit with an event handler whose parameter is a TypedDict with required keys, while the form lacks inputs whose static name/id equals those keys — e.g. controls with dynamic/placeholder names, or fields rendered conditionally.

Common situations: Adding a required TypedDict field but forgetting to add the input; using rx.input(name=State.dynamic_name) so the name is not static; renaming TypedDict keys without renaming inputs.

Related errors


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