{"record":{"id":"455c6e913191b747","repo":"deepset-ai/haystack","slug":"mutating-attribute-name-on-an-instance-of-ty","errorCode":null,"errorMessage":"Mutating attribute '{name}' on an instance of '{type(self).__name__}' can lead to unexpected behavior by affecting other parts of the pipeline that use the same dataclass instance. Use `dataclasses.replace(instance, {name}=new_value)` instead. See https://docs.haystack.deepset.ai/docs/custom-components#requirements for details.","messagePattern":"Mutating attribute '(.+?)' on an instance of '(.+?)' can lead to unexpected behavior by affecting other parts of the pipeline that use the same dataclass instance\\. Use `dataclasses\\.replace\\(instance, (.+?)=new_value\\)` instead\\. See https://docs\\.haystack\\.deepset\\.ai/docs/custom-components#requirements for details\\.","errorType":"console","errorClass":"Warning","httpStatus":null,"severity":"warning","filePath":"haystack/utils/dataclasses.py","lineNumber":41,"sourceCode":"    @wraps(original_init)\n    def __init_track__(self: T, *args: Any, **kwargs: Any) -> None:\n        # We don't raise warnings during initialization, i.e. during the first call to __init__ and __post_init__.\n        initializing.add(id(self))\n        try:\n            return original_init(self, *args, **kwargs)\n        finally:\n            initializing.discard(id(self))\n\n    @wraps(original_setattr)\n    def __setattr_warn__(self: T, name: str, value: Any) -> None:\n        # We raise warnings if the dataclass is mutated in-place after initialization.\n        if (\n            id(self) not in initializing\n            and name in getattr(self, \"__dataclass_fields__\", {})\n            and name in getattr(self, \"__dict__\", {})\n        ):\n            # We raise a warning if the attribute is a dataclass field and a dictionary key.\n            warnings.warn(\n                f\"Mutating attribute '{name}' on an instance of \"\n                f\"'{type(self).__name__}' can lead to unexpected behavior by affecting other parts of the pipeline \"\n                \"that use the same dataclass instance. \"\n                f\"Use `dataclasses.replace(instance, {name}=new_value)` instead. \"\n                \"See https://docs.haystack.deepset.ai/docs/custom-components#requirements for details.\",\n                Warning,\n                stacklevel=2,\n            )\n        # mypy infers original_setattr as bound to the type, expecting (str, Any), we call the unbound form\n        return original_setattr(self, name, value)  # type: ignore[call-arg, arg-type]\n\n    # mypy considers direct dunder access on a class unsound, ruff prefers direct access\n    cls.__init__ = __init_track__  # type: ignore[misc]\n    # mypy does not allow assigning to a method, ruff prefers direct access\n    cls.__setattr__ = __setattr_warn__  # type: ignore[method-assign, assignment]\n    return cls\n","sourceCodeStart":23,"sourceCodeEnd":58,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/utils/dataclasses.py#L23-L58","documentation":"Haystack emits this warning from haystack/utils/dataclasses.py:41 when you assign to a dataclass field on an instance that is shared across pipeline components. Mutating the instance affects every pipeline part holding a reference to the same object, causing hard-to-trace bugs; Haystack recommends creating a new instance with dataclasses.replace().","triggerScenarios":"Attribute assignment like obj.field = new_value on a dataclass instance decorated with Haystack's mutation-warning mechanism, where name is both a __dataclass_fields__ entry and present in the instance __dict__, outside initialization (id(self) not tracked in the initializing set).","commonSituations":"Modifying a shared ChatMessage, generated response object, or component output in-place before passing it downstream; caching pipeline results and tweaking fields; two branches of a pipeline accidentally mutating the same dataclass instance.","solutions":["Replace in-place mutation with dataclasses.replace(instance, field_name=new_value) to create a new instance","Copy the instance first (copy.deepcopy or dataclasses.replace with no changes) before mutating","Restructure pipeline so shared dataclass instances are treated as immutable; pass new instances downstream","Suppress via warnings.filterwarnings('ignore', category=Warning, module='haystack') only after confirming no other pipeline component shares the instance"],"exampleFix":"# before\nmessage.text = message.text + \" (edited)\"  # affects all holders of `message`\npipeline.run(data={...})\n# after\nedited = dataclasses.replace(message, text=message.text + \" (edited)\")\npipeline.run(data={\"branch\": edited})","handlingStrategy":"fallback","validationCode":"import dataclasses\n\ndef assert_mutation_safe(instance, name: str) -> None:\n    fields = getattr(instance, \"__dataclass_fields__\", {})\n    if name in fields:\n        raise ValueError(\n            f\"'{name}' is a shared dataclass field; use dataclasses.replace() instead of assignment\"\n        )","typeGuard":"import dataclasses\n\ndef is_shared_dataclass_field(instance, name: str) -> bool:\n    return (\n        name in getattr(instance, \"__dataclass_fields__\", {})\n        and name in getattr(instance, \"__dict__\", {})\n    )","tryCatchPattern":"import warnings\n\nwith warnings.catch_warnings(record=True) as caught:\n    warnings.simplefilter(\"always\")\n    obj.field = new_value\n    if any(\"Mutating attribute\" in str(w.message) for w in caught):\n        obj = dataclasses.replace(obj, field=new_value)  # fallback: create a new instance","preventionTips":["Treat pipeline dataclass instances (messages, results) as immutable","Always use dataclasses.replace(instance, field=...) instead of attribute assignment","Deep-copy an instance before mutating it if you must modify it","Enable -W error in tests so accidental in-place mutation of shared fields fails immediately"],"tags":["dataclass","immutability","python","pipeline"],"backgroundTag":"dataclass-mutation-warning","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}