{"id":"c8995c0e710b8e71","repo":"pytest-dev/pytest","slug":"target-r-has-no-attribute-name-r","errorCode":null,"errorMessage":"{target!r} has no attribute {name!r}","messagePattern":"(.+?) has no attribute (.+?)","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/monkeypatch.py","lineNumber":242,"sourceCode":"            if not isinstance(target, str):\n                raise TypeError(\n                    \"use setattr(target, name, value) or \"\n                    \"setattr(target, value) with target being a dotted \"\n                    \"import string\"\n                )\n            value = name\n            name, target = derive_importpath(target, raising)\n        else:\n            if not isinstance(name, str):\n                raise TypeError(\n                    \"use setattr(target, name, value) with name being a string or \"\n                    \"setattr(target, value) with target being a dotted \"\n                    \"import string\"\n                )\n\n        oldval = getattr(target, name, NOTSET)\n        if raising and oldval is NOTSET:\n            raise AttributeError(f\"{target!r} has no attribute {name!r}\")\n\n        # avoid class descriptors like staticmethod/classmethod\n        if inspect.isclass(target):\n            oldval = target.__dict__.get(name, NOTSET)\n        setattr(target, name, value)\n        self._setattr.append((target, name, oldval))\n\n    def delattr(\n        self,\n        target: object | str,\n        name: str | NotSetType = NOTSET,\n        raising: bool = True,\n    ) -> None:\n        \"\"\"Delete attribute ``name`` from ``target``.\n\n        If no ``name`` is specified and ``target`` is a string\n        it will be interpreted as a dotted import path with the\n        last part being the attribute name.","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/monkeypatch.py#L224-L260","documentation":"In `monkeypatch.setattr`, after resolving target+name, pytest checks the existing attribute. With `raising=True` (the default), if the attribute is not present on the target it raises `AttributeError: <target> has no attribute <name>`. This is the guard against patching non-existent attributes, which usually signals a typo or wrong patch location.","triggerScenarios":"`monkeypatch.setattr(myobj, 'nonexistent', fake)` with default `raising=True`; patching an attribute introduced only after some runtime setup that hasn't happened.","commonSituations":"Patching the wrong module (the classic 'where to patch' problem — patching the using module vs the defining module); typos; version skew where the attribute was removed.","solutions":["Patch the name where it is looked up (e.g. `mypkg.mod.os` if `myobj` does `import os` there).","Set `raising=False` if you intentionally create a new attribute.","Verify with `hasattr(target, name)` before patching to get a clearer assertion."],"exampleFix":"# before\nmonkeypatch.setattr(mymod, 'requests.get', fake)  # nested path not an attribute\n# after\nimport mymod.requests as r\nmonkeypatch.setattr(r, 'get', fake)","handlingStrategy":"validation","validationCode":"def safe_to_patch(target, name) -> bool:\n    return hasattr(target, name)","typeGuard":null,"tryCatchPattern":"try:\n    monkeypatch.setattr(target, name, fake)\nexcept AttributeError:\n    # patch the using module instead, or pass raising=False to create\n    monkeypatch.setattr(target, name, fake, raising=False)","preventionTips":["Patch the name where it is looked up, not where it is defined.","Use `hasattr` checks or `raising=False` for genuinely new attributes.","Confirm the attribute exists after the runtime setup that defines it."],"tags":["pytest","monkeypatch","attribute-error","setattr"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}