reflex-dev/reflex · warning · ValueError

Root state must be provided to convert legacy token to BaseS

Error message

Root state must be provided to convert legacy token to BaseStateToken.

What it means

BaseStateToken.from_legacy_token converts an old-style string token into a BaseStateToken and needs the root state class to resolve substate names — without it there is no way to look up state classes, so it raises ValueError. It also emits a deprecation warning because passing plain strings to modify_state is deprecated (removal in 1.0).

Source

Thrown at reflex/istate/manager/token.py:233

        The legacy token format is "{ident}_{module_path}.{class_name}".

        Args:
            legacy_token: The legacy token string to convert.
            root_state: The root state instance.

        Returns:
            A BaseStateToken instance created from the legacy token.

        Raises:
            ValueError: If the legacy token format is invalid or if the state class cannot be found
        """
        from reflex.state import _split_substate_key

        if root_state is None:
            msg = (
                "Root state must be provided to convert legacy token to BaseStateToken."
            )
            raise ValueError(msg)

        console.deprecate(
            feature_name="Passing a string to modify_state",
            reason="Use rx.BaseStateToken(token, state_cls) instead of the legacy string format",
            deprecation_version="0.9.0",
            removal_version="1.0",
        )

        client_token, state_path = _split_substate_key(legacy_token)
        state_cls = root_state.get_class_substate(tuple(state_path.split(".")))  # type: ignore[union-attr]
        return cls(ident=client_token, cls=state_cls)

View on GitHub (pinned to 45b8ed5ab7)

Solutions

  1. Pass a state class alongside the token: modify_state(token_string, rx.BaseState) or provide root_state to from_legacy_token
  2. Better: migrate to rx.BaseStateToken(token, StateCls) instead of the legacy string format before 1.0
  3. If wrapping modify_state generically, thread the state_cls/root_state through your abstraction

Example fix

# before
async with state.modify_state("legacy_token_string") as state:
    ...

# after
async with state.modify_state(BaseStateToken("legacy_token_string", rx.BaseState)) as state:
    ...
Defensive patterns

Strategy: validation

Validate before calling

if isinstance(token, str) and root_state is None:
    raise ValueError("legacy string token requires a state class")  # fail fast with a clear message

Type guard

def is_legacy_string_token(token: object) -> TypeGuard[str]:
    return isinstance(token, str)

Prevention

When it happens

Trigger: Calling from_legacy_token(token_string, root_state=None), most often indirectly via modify_state("token_string") without a state class so no root can be inferred. Custom middleware that stored string tokens and calls the conversion with only the string hits this directly.

Common situations: Upgrading an app/session layer that persisted legacy string tokens; middleware or auth code that calls modify_state with a raw token string and no state_cls; mixing pre-0.9 token formats with the new BaseStateToken API.

Related errors


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