{"record":{"id":"3dbdbd25cb4623f3","repo":"Textualize/textual","slug":"can-t-set-obj-self-name-r-reactive-attributes","errorCode":null,"errorMessage":"Can't set {obj}.{self.name!r}; reactive attributes with a compute method are read-only","messagePattern":"Can't set (.+?)\\.(.+?); reactive attributes with a compute method are read-only","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"src/textual/reactive.py","lineNumber":331,"sourceCode":"        else:\n            return getattr(obj, internal_name)\n\n    def _set(self, obj: Reactable, value: ReactiveType, always: bool = False) -> None:\n        _rich_traceback_omit = True\n\n        if not hasattr(obj, \"_id\"):\n            raise ReactiveError(\n                f\"Node is missing data; Check you are calling super().__init__(...) in the {obj.__class__.__name__}() constructor, before setting reactives.\"\n            )\n\n        if isinstance(value, _Mutated):\n            value = value.value\n            always = True\n\n        self._initialize_reactive(obj, self.name)\n\n        if hasattr(obj, self.compute_name):\n            raise AttributeError(\n                f\"Can't set {obj}.{self.name!r}; reactive attributes with a compute method are read-only\"\n            )\n\n        name = self.name\n        current_value = getattr(obj, name)\n        # Check for private and public validate functions.\n        private_validate_function = getattr(obj, f\"_validate_{name}\", None)\n        if callable(private_validate_function):\n            value = private_validate_function(value)\n        public_validate_function = getattr(obj, f\"validate_{name}\", None)\n        if callable(public_validate_function):\n            value = public_validate_function(value)\n\n        # Toggle the classes using the value's truthiness\n        if (toggle_class := self._toggle_class) is not None:\n            obj.set_class(bool(value), *toggle_class.split())\n\n        # If the value has changed, or this is the first time setting the value","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/reactive.py#L313-L349","documentation":"Textual reactives backed by a compute_ method are derived values and are read-only. Assigning to such a reactive raises AttributeError.","triggerScenarios":"A class defines both a reactive 'foo' and a 'watch_foo'/compute method chain where 'foo' has a compute method (compute_foo), and code assigns widget.foo = value.","commonSituations":"Trying to override or reset a computed reactive (like a computed layout property) from an event handler or test setup.","solutions":["Remove the assignment; change the inputs the compute method depends on instead","If the reactive should be writable, delete the compute_ method and rely on watch_/validate_ hooks","Use set_reactive() only if you truly need to bypass watchers, understanding computed values are still re-derived"],"exampleFix":"# before\nclass Sidebar(Widget):\n    width_ratio = reactive(0.3)\n    def compute_width_ratio(self): return self.expanded and 0.5 or 0.3\nsidebar.width_ratio = 0.5  # raises\n# after\nclass Sidebar(Widget):\n    expanded = reactive(False)\n    width_ratio = reactive(0.3)\n    def compute_width_ratio(self): return 0.5 if self.expanded else 0.3\nsidebar.expanded = True","handlingStrategy":"type-guard","validationCode":"from textual.reactive import Reactive\nif hasattr(owner, f\"compute_{name}\"):\n    raise AttributeError(f\"{name} is computed and read-only\")","typeGuard":"def is_writable_reactive(obj: object, name: str) -> bool:\n    return not hasattr(obj, f\"compute_{name}\")","tryCatchPattern":"try:\n    widget.foo = value\nexcept AttributeError as e:\n    if \"compute method\" in str(e):\n        change_foo_inputs_instead()","preventionTips":["Name compute methods compute_<attr> deliberately","Document computed reactives as derived state"],"tags":["reactive","read-only","computed","textual"],"backgroundTag":"readonly-property-assignment","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}