# Buggy code
def recursive_set_attributes(module, b_path, follow, file_args):
    changed = False
    for b_root, b_dirs, b_files in os.walk(b_path):
        for b_fsobj in b_dirs + b_files:
            b_fsname = os.path.join(b_root, b_fsobj)
            if not os.path.islink(b_fsname):
                tmp_file_args = file_args.copy()
                tmp_file_args["path"] = to_native(
                    b_fsname, errors="surrogate_or_strict"
                )
                changed |= module.set_fs_attributes_if_different(
                    tmp_file_args, changed, expand=False
                )
            else:
                tmp_file_args = file_args.copy()
                tmp_file_args["path"] = to_native(
                    b_fsname, errors="surrogate_or_strict"
                )
                changed |= module.set_fs_attributes_if_different(
                    tmp_file_args, changed, expand=False
                )
                if follow:
                    b_fsname = os.path.join(b_root, os.readlink(b_fsname))
                    if os.path.isdir(b_fsname):
                        changed |= recursive_set_attributes(
                            module, b_fsname, follow, file_args
                        )
                    tmp_file_args = file_args.copy()
                    tmp_file_args["path"] = to_native(
                        b_fsname, errors="surrogate_or_strict"
                    )
                    changed |= module.set_fs_attributes_if_different(
                        tmp_file_args, changed, expand=False
                    )
    return changed


# Traceback (OSError)
$ ansible-playbook -i localhost, symlink_to_nowhere-playbook.yaml

PLAY [localhost] *****************************************************************

TASK [Gathering Facts] ***********************************************************
ok: [localhost]

TASK [Create a directory for testing] ********************************************
changed: [localhost]

TASK [Create a symlink from a nonexistant file] **********************************
[WARNING]: Consider using the file module with state=link rather than running
ln.  If you need to use command because file is insufficient you can add
warn=False to this command task or set command_warnings=False in ansible.cfg to
get rid of this message.

changed: [localhost]

TASK [Attempt to change ownership on directory] **********************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: OSError: [Errno 2] No such file or directory: '/doesnt_exist'
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_ZOFwo4/ansible_module_file.py\", line 473, in <module>\n    main()\n  File \"/tmp/ansible_ZOFwo4/ansible_module_file.py\", line 350, in main\n    changed |= recursive_set_attributes(module, to_bytes(file_args['path'], errors='surrogate_or_strict'), follow, file_args)\n  File \"/tmp/ansible_ZOFwo4/ansible_module_file.py\", line 172, in recursive_set_attributes\n    changed |= module.set_fs_attributes_if_different(tmp_file_args, changed, expand=False)\n  File \"/tmp/ansible_ZOFwo4/ansible_modlib.zip/ansible/module_utils/basic.py\", line 1507, in set_fs_attributes_if_different\n  File \"/tmp/ansible_ZOFwo4/ansible_modlib.zip/ansible/module_utils/basic.py\", line 1164, in set_owner_if_different\n  File \"/tmp/ansible_ZOFwo4/ansible_modlib.zip/ansible/module_utils/basic.py\", line 1059, in user_and_group\nOSError: [Errno 2] No such file or directory: '/doesnt_exist'\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 1}
to retry, use: --limit @/users/zdw/foo/symlink_to_nowhere-playbook.retry

PLAY RECAP ***********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=1

# Fixed code
def recursive_set_attributes(module, b_path, follow, file_args):
    changed = False
    for b_root, b_dirs, b_files in os.walk(b_path):
        for b_fsobj in b_dirs + b_files:
            b_fsname = os.path.join(b_root, b_fsobj)
            if not os.path.islink(b_fsname):
                tmp_file_args = file_args.copy()
                tmp_file_args["path"] = to_native(
                    b_fsname, errors="surrogate_or_strict"
                )
                changed |= module.set_fs_attributes_if_different(
                    tmp_file_args, changed, expand=False
                )
            else:
                # Change perms on the link
                tmp_file_args = file_args.copy()
                tmp_file_args["path"] = to_native(
                    b_fsname, errors="surrogate_or_strict"
                )
                changed |= module.set_fs_attributes_if_different(
                    tmp_file_args, changed, expand=False
                )

                if follow:
                    b_fsname = os.path.join(b_root, os.readlink(b_fsname))
                    # The link target could be nonexistent
                    if os.path.exists(b_fsname):
                        if os.path.isdir(b_fsname):
                            # Link is a directory so change perms on the directory's contents
                            changed |= recursive_set_attributes(
                                module, b_fsname, follow, file_args
                            )

                        # Change perms on the file pointed to by the link
                        tmp_file_args = file_args.copy()
                        tmp_file_args["path"] = to_native(
                            b_fsname, errors="surrogate_or_strict"
                        )
                        changed |= module.set_fs_attributes_if_different(
                            tmp_file_args, changed, expand=False
                        )
    return changed