Skip to content

0.9.12a1: metaclass conflict for State subclasses using a BaseStateMeta-derived metaclass (breaks reflex-enterprise 0.9.5 auth/MCP/REST apps at startup) #7211

Description

@masenf

Describe the bug

Since #7136, rx.State's metaclass is reflex.istate.validation._StateMeta (a subclass of BaseStateMeta) instead of BaseStateMeta itself (reflex/state.py:629 class BaseState(EvenMoreBasicBaseState, metaclass=_StateMeta), _StateMeta defined at reflex/istate/validation.py:90). Any downstream metaclass written as class MyMeta(BaseStateMeta) — the only spelling that worked through 0.9.11.post1, and BaseStateMeta is exported in reflex_base.vars.__all__ — can no longer be used on a State subclass, because two sibling subclasses of BaseStateMeta cannot both be the metaclass of one class:

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

reflex-enterprise 0.9.5 does exactly this (reflex_enterprise/auth/oidc/state.py:347 class OIDCCookieMeta(BaseStateMeta), used at line 381 by OIDCAuthState). Because MCPPlugin.post_compile and EventHandlerAPIPlugin.post_compile import the OIDC module, every app using rxe.AuthPlugin(), rxe.MCPPlugin() or rxe.EventHandlerAPIPlugin() dies at startup on 0.9.12a1 — the shipped demos/oidc and demos/tickets included — while the vite frontend keeps serving 200, so the page loads and shows "Cannot connect to server: websocket error". reflex-enterprise 0.9.5 pins reflex[db]>=0.9.6 with no upper bound, so a routine pip install -U reflex after 0.9.12 ships would break deployed apps. The metaclass change is not mentioned in the changelog.

To Reproduce

Framework only, no enterprise needed (uv pip install --prerelease=allow 'reflex==0.9.12a1'):

import reflex as rx
from reflex.vars import BaseStateMeta


class CookieMeta(BaseStateMeta):
    def __new__(mcs, name, bases, attrs, **kwargs):
        attrs.setdefault("added_by_meta", "yes")
        return super().__new__(mcs, name, bases, attrs, **kwargs)


class S(rx.State, mixin=True, metaclass=CookieMeta):  # fails without mixin=True too
    x: int = 0
  • 0.9.12a1: TypeError: metaclass conflict ...
  • 0.9.11.post1: works (type(rx.State) is BaseStateMeta)
  • class M(type(rx.State)) works on both versions, but no published downstream code spells it that way.

Enterprise: in a venv with reflex 0.9.12a1 + reflex-enterprise[mcp]==0.9.5, python -c "import reflex_enterprise.auth.oidc.state" raises the same TypeError, and reflex run on an app with only rxe.MCPPlugin() never binds the backend port (22 other enterprise modules import fine; 23/23 import on 0.9.11.post1).

Expected behavior

A metaclass derived from BaseStateMeta keeps working on State subclasses, as it did through 0.9.11.post1 — or the change is announced as breaking, with the type(rx.State) spelling documented.

Suggested fix shape

Perform the #7136 reserved-name validation inside BaseStateMeta.__new__ (guarded on "a base is a BaseState") so rx.State keeps BaseStateMeta as its metaclass, or make the validating metaclass compose with sibling BaseStateMeta subclasses. Regression test: the snippet above must construct. reflex-enterprise can independently switch to class OIDCCookieMeta(type(rx.State)), but that does not rescue the already-published 0.9.5 against a released 0.9.12.

Specifics (please complete the following information):

  • Python Version: 3.11.15 (also reproduced on 3.10 and 3.12–3.15)
  • Reflex Version: 0.9.12a1 / reflex-base 0.9.12a1 (regression vs 0.9.11.post1)
  • OS: Linux (Ubuntu 24.04 container)

Additional context

Found by the 0.9.12a1 pre-release QA campaign (FINDING-001), reproduced by the orchestrator, two explorer agents and an adversarial verifier. Evidence and scripts: prerelease-testing/2026-09-18-v0.9.12a1/ on branch claude/reflex-prerelease-testing-t0sd90orch_probes/metaclass_probe.py, ent_mcp_oidc/NOTES.md, ent_map_dnd_flow_mantine/logs/tickets_new_dev.TRACEBACK.txt, and FINDINGS.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions