ansible.posix.synchronize task failed with error 'Connection' object has no attribute '_new_stdin'
I have open sourced some Ansible roles I use for my personal projects. Everything was working well until one day running my playbook would always fail on the ansible.posix.synchronize task.
TASK [websites : push example.com contents] ************************************************************************************************************************** [ERROR]: Task failed: 'Connection' object has no attribute '_new_stdin' Origin: /Users/aikchar/ansible-roles/websites/tasks/rhel.yml:18:3 16 become: yes 17 18 - name: "push {{ domain_name }} contents" ^ column 3 fatal: [example.com]: FAILED! => {"changed": false, "msg": "Task failed: 'Connection' object has no attribute '_new_stdin'"}
The Fix
The better fix is to remove older collections. Read on to learn why this worked for me.
$ mv /Users/aikchar/.ansible/collections/ansible_collections /Users/aikchar/.ansible/collections/old_ansible_collections
With that fix applied,
TASK [websites : push example.com contents] *************************************************************************************************************** changed: [example.com]
Alternative Fix
Set custom value of collections_path in ansible.cfg (/Users/aikchar/infra-as-code/ansible.cfg),
[defaults] collections_path=/usr/local/Cellar/ansible/13.1.0/libexec/lib/python3.14/site-packages ...
Notice I am using Homebrew to install Ansible. Your path may be different if you are using a different Python installation, e.g. using virtualenvs.
The disadvantage of this fix is that everytime I upgrade Ansible, I have to modify this file.
Another thing to keep in mind is that the value should not be put into quotes, whether single or double. It causes a very weird issue,
# Without quotes (as it should be) $ ansible-config dump | grep COLLECTIONS_PATHS COLLECTIONS_PATHS(/Users/aikchar/infra-as-code/ansible.cfg) = ['/usr/local/Cellar/ansible/13.1.0/libexec/lib/python3.14/site-packages']
# With quotes (DO NOT DO) $ ansible-config dump | grep COLLECTIONS_PATHS COLLECTIONS_PATHS(/Users/aikchar/infra-as-code/ansible.cfg) = ["/Users/aikchar/infra-as-code/'/usr/local/Cellar/ansible/13.1.0/libexec/lib/python3.14/site-packages'"]
The Reason
The default value of collections_path is {{ ANSIBLE_HOME ~ "/collections:/usr/share/ansible/collections" }}.
I had not set ANSIBLE_HOME environment variable, in which case it's set to
the user's home directory (/Users/aikchar/.ansible), as seen below,
$ ansible-config dump | grep COLLECTIONS_PATHS COLLECTIONS_PATHS(default) = ['/Users/aikchar/.ansible/collections', '/usr/share/ansible/collections']
At some point in 2022 I had installed Ansible collection for synchronize in this default path. I don't remember when or how or why; it was just there.
$ ls -lA /Users/aikchar/.ansible/collections/ansible_collections/ansible/posix/plugins/action total 48 -rw-r--r-- 1 aikchar staff 0 Feb 10 2022 __init__.py -rw-r--r-- 1 aikchar staff 2658 Feb 10 2022 patch.py -rw-r--r-- 1 aikchar staff 20202 Feb 10 2022 synchronize.py
The culprit was right in front of me, the version was from 2021.
$ grep -B5 -A5 new_stdin /Users/aikchar/.ansible/collections/ansible_collections/ansible/posix/plugins/action/synchronize.py # Delegate to localhost as the source of the rsync unless we've been # told (via delegate_to) that a different host is the source of the # rsync if not use_delegate and remote_transport: # Create a connection to localhost to run rsync on new_stdin = self._connection._new_stdin # Unlike port, there can be only one shell localhost_shell = None for host in C.LOCALHOST: localhost_vars = task_vars['hostvars'].get(host, {}) -- break else: localhost_executable = C.DEFAULT_EXECUTABLE self._play_context.executable = localhost_executable new_connection = connection_loader.get('local', self._play_context, new_stdin) self._connection = new_connection # Override _remote_is_local as an instance attribute specifically for the synchronize use case # ensuring we set local tmpdir correctly self._connection._remote_is_local = True self._override_module_replaced_vars(task_vars)
Since there was a matching collection in the default path, it was being used instead of the ones packaged in the Ansible installation,
$ ls -lA /usr/local/Cellar/ansible/13.1.0/libexec/lib/python3.14/site-packages/ansible_collections/ansible/posix/plugins/action total 56 -rw-r--r-- 1 aikchar admin 0 Dec 9 09:58 __init__.py -rw-r--r-- 1 aikchar admin 2658 Dec 9 09:58 patch.py -rw-r--r-- 1 aikchar admin 20928 Dec 9 09:58 synchronize.py
In 2023, new_stdin was deprecated by Ansible. Following that, synchronize removed new_stdin in 2024. So even though the newer versions of Ansible were packaging the version of synchronize that didn't have new_stdin, I was still running into the error.
Workaround
Until I had figured out how to fix the issue, I still needed to be able to push
contents of the website to the server. A manual workaround I used was to run
rsync like so,
rsync -rv --delete --no-perms -e 'ssh -F /Users/aikchar/infra-as-code/ssh_config' --rsync-path 'sudo /usr/bin/rsync' /Users/aikchar/website-example.com/output/ aikchar@example.com:/srv/www/example.com/