{"record":{"id":"b22320a98baf1041","repo":"reflex-dev/reflex","slug":"cannot-assign-the-same-cached-property-to-two-diff","errorCode":null,"errorMessage":"Cannot assign the same cached_property to two different names ({self._attrname!r} and {name!r}).","messagePattern":"Cannot assign the same cached_property to two different names \\((.+?) and (.+?)\\)\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/reflex-base/src/reflex_base/vars/base.py","lineNumber":2051,"sourceCode":"                try:\n                    unique_id = object.__getattribute__(this, cached_field_name)\n                except AttributeError:\n                    if original_del is not None:\n                        original_del(this)\n                    return\n                GLOBAL_CACHE.pop(unique_id, None)\n\n                if original_del is not None:\n                    original_del(this)\n\n            owner.__del__ = delete_property\n\n        elif name != self._attrname:\n            msg = (\n                \"Cannot assign the same cached_property to two different names \"\n                f\"({self._attrname!r} and {name!r}).\"\n            )\n            raise TypeError(msg)\n\n    def __get__(self, instance: Any, owner: type | None = None):\n        \"\"\"Get the cached property.\n\n        Args:\n            instance: The instance to get the cached property from.\n            owner: The owner of the cached property.\n\n        Returns:\n            The cached property.\n\n        Raises:\n            TypeError: If the class does not have __set_name__.\n        \"\"\"\n        if self._attrname is None:\n            msg = \"Cannot use cached_property on a class without __set_name__.\"\n            raise TypeError(msg)\n        cached_field_name = \"_reflex_cache_\" + self._attrname","sourceCodeStart":2033,"sourceCodeEnd":2069,"githubUrl":"https://github.com/reflex-dev/reflex/blob/45b8ed5ab735f8a56bbb09a42384f030eb0208e7/packages/reflex-base/src/reflex_base/vars/base.py#L2033-L2069","documentation":"This is CPython's cached_property guard reimplemented in Reflex: a single cached_property descriptor instance was assigned to two different attributes of the same class. Python's cached_property stores its attribute name in _attrname when __set_name__ runs, and refuses to be reused under a second name. Reflex's copy preserves that invariants check.","triggerScenarios":"Assigning the same cached_property object to two class attributes, e.g. cp = _cached_property(f); class S: a = cp; b = cp, or dynamically attaching one ComputedVar/cached property object to multiple attribute names in a State class (e.g. via setattr in a loop or metaprogramming).","commonSituations":"Metaprogramming over State classes that copies ComputedVar/cached_property descriptors between attributes; applying the same decorator result to multiple methods; refactoring state mixins where a property object gets aliased instead of re-created.","solutions":["Create a fresh property for each attribute (call the decorator/factory once per name, or copy.copy the descriptor before reassigning)","If aliasing is intentional, use functools.cached_property semantics correctly: re-run the decorator for the second name instead of assigning the same object","Find the duplicate assignment with grep for the attribute/property name in class bodies and metaprogramming code"],"exampleFix":"# before\nmake_prop = _cached_property(some_fget)\nclass MyState(rx.State):\n    a = make_prop\n    b = make_prop  # TypeError\n\n# after\nclass MyState(rx.State):\n    a = _cached_property(some_fget)\n    b = _cached_property(other_fget)  # or the same callable, new descriptor\n\n# or alias the result, not the descriptor:\n#   b = property(lambda self: self.a)","handlingStrategy":"validation","validationCode":"def validate_cached_props(cls) -> None:\n    seen = {}\n    for name in vars(cls):\n        obj = vars(cls)[name]\n        attrname = getattr(obj, '_attrname', None)\n        if attrname is not None and obj is not getattr(cls, attrname, obj):\n            pass\n        if id(obj) in seen:\n            raise TypeError(f'{type(obj).__name__} assigned to both {seen[id(obj)]!r} and {name!r}')\n        seen[id(obj)] = name","typeGuard":"def is_fresh_descriptor(obj, assigned_names: set[str]) -> bool:\n    # a cached_property may only be assigned to one name per class\n    return id(obj) not in assigned_names","tryCatchPattern":null,"preventionTips":["Never assign one decorator result to multiple attributes; run the decorator once per name","In metaprogramming, use copy.copy(descriptor) before reassigning to a new name","Lint dynamically generated State classes for duplicate descriptor object ids"],"tags":["reflex","cached-property","descriptor","metaprogramming","typeerror"],"backgroundTag":"descriptor-reuse-in-class","analyzedSha":"45b8ed5ab735f8a56bbb09a42384f030eb0208e7","analyzedAt":"2026-08-28T19:25:27.644Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}