# Python \`passlib\` and \`bcrypt\` issue and patch

**URL:** <https://forum.ansible.com/t/python-passlib-and-bcrypt-issue-and-patch/44640>\
**Category:** Get Help\
**Created:** [October 8, 2025, 4:01pm UTC](https://forum.ansible.com/t/python-passlib-and-bcrypt-issue-and-patch/44640 "2025-10-08T16:01:12Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![glitch](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/glitch/32/9605_2.png) [@glitch](https://forum.ansible.com/u/glitch)\
**Post date:** [October 8, 2025, 4:01pm UTC](https://forum.ansible.com/t/python-passlib-and-bcrypt-issue-and-patch/44640/1 "2025-10-08T16:01:12Z")

</div>

I got bit by the change to `bcrypt` in version 5.0.0 that broke `passlib`:

> <https://github.com/ansible/ansible/issues/85919>
>
> \### Summary
> 
> Got following error with bcrypt==5.0.0.
> 
> \`\`\`
> \[ERROR\]: Task failed: …Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': The filter plugin 'ansible.builtin.password\_hash' failed: Could not hash the secret: password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my\_password\[:72\])
> 
> Task failed: Finalization of task args for 'ansible.builtin.debug' failed.
> Origin: /opt/configuration/environments/kolla/playbook-x.yml:4:7
> 
> 2 gather\_facts: false
> 3 tasks:
> 4 - debug:
> ^ column 7
> 
> \<\<\< caused by \>\>\>
> 
> Error while resolving value for 'msg': The filter plugin 'ansible.builtin.password\_hash' failed: Could not hash the secret: password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my\_password\[:72\])
> Origin: /opt/configuration/environments/kolla/playbook-x.yml:5:14
> 
> 3 tasks:
> 4 - debug:
> 5 msg: "{{ 'vagrant-libvirt' | password\_hash(hashtype='bcrypt') }}"
> ^ column 14
> 
> fatal: \[localhost\]: FAILED! =\> {"msg": "Task failed: Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': The filter plugin 'ansible.builtin.password\_hash' failed: Could not hash the secret: password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my\_password\[:72\])"}
> \`\`\`
> 
> Test play:
> 
> \`\`\`
> \- hosts: localhost
> gather\_facts: false
> tasks:
> - debug:
> msg: "{{ 'vagrant-libvirt' | password\_hash(hashtype='bcrypt') }}"
> \`\`\`
> 
> Tested with:
> 
> \`\`\`
> ansible-core 2.19.2
> bcrypt 5.0.0
> passlib 1.7.4
> \`\`\`
> 
> Works with:
> 
> \`\`\`
> ansible-core 2.19.2
> bcrypt 4.3.0
> passlib 1.7.4
> \`\`\`
> 
> \`\`\`
> PLAY \[localhost\] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
> 
> TASK \[debug\] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
> ok: \[localhost\] =\> {
> "msg": "$2b$12$OBcE8LP/V6mIm2GEnoperegQoN9/8dTUcBVsLn/X9FbD5WCUGoU3e"
> }
> 
> PLAY RECAP \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
> localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
> \`\`\`
> 
> \### Issue Type
> 
> Bug Report
> 
> \### Component Name
> 
> ansible.builtin.password\_hash
> 
> \### Ansible Version
> 
> \`\`\`console
> $ ansible --version
> ansible \[core 2.19.2\]
> config file = /etc/ansible/ansible.cfg
> configured module search path = \['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'\]
> ansible python module location = /usr/local/lib/python3.13/site-packages/ansible
> ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
> executable location = /usr/local/bin/ansible
> python version = 3.13.7 (main, Sep 8 2025, 21:46:19) \[GCC 12.2.0\] (/usr/local/bin/python3.13)
> jinja version = 3.1.6
> pyyaml version = 6.0.2 (with libyaml v0.2.5)
> \`\`\`
> 
> \### Configuration
> 
> \`\`\`console
> \# if using a version older than ansible-core 2.12 you should omit the '-t all'
> $ ansible-config dump --only-changed -t all
> CACHE\_PLUGIN(/etc/ansible/ansible.cfg) = jsonfile
> CACHE\_PLUGIN\_CONNECTION(/etc/ansible/ansible.cfg) = /ansible/cache
> CONFIG\_FILE() = /etc/ansible/ansible.cfg
> DEFAULT\_ACTION\_PLUGIN\_PATH(/etc/ansible/ansible.cfg) = \['/ansible/action\_plugins'\]
> DEFAULT\_FORKS(/etc/ansible/ansible.cfg) = 50
> DEFAULT\_GATHERING(/etc/ansible/ansible.cfg) = smart
> DEFAULT\_LOG\_PATH(/etc/ansible/ansible.cfg) = /ansible/logs/ansible.log
> DEFAULT\_PRIVATE\_KEY\_FILE(/etc/ansible/ansible.cfg) = /ansible/secrets/id\_rsa
> DEFAULT\_ROLES\_PATH(/etc/ansible/ansible.cfg) = \['/ansible/roles', '/ansible/galaxy'\]
> DEPRECATION\_WARNINGS(/etc/ansible/ansible.cfg) = False
> HOST\_KEY\_CHECKING(/etc/ansible/ansible.cfg) = False
> RETRY\_FILES\_ENABLED(/etc/ansible/ansible.cfg) = False
> 
> GALAXY\_SERVERS:
> 
> 
> CACHE:
> =====
> 
> jsonfile:
> \_\_\_\_\_\_\_\_
> \_uri(/etc/ansible/ansible.cfg) = /ansible/cache
> 
> CONNECTION:
> ==========
> 
> paramiko\_ssh:
> \_\_\_\_\_\_\_\_\_\_\_\_
> host\_key\_checking(/etc/ansible/ansible.cfg) = False
> private\_key\_file(/etc/ansible/ansible.cfg) = /ansible/secrets/id\_rsa
> 
> ssh:
> \_\_\_
> host\_key\_checking(/etc/ansible/ansible.cfg) = False
> pipelining(/etc/ansible/ansible.cfg) = True
> private\_key\_file(/etc/ansible/ansible.cfg) = /ansible/secrets/id\_rsa
> 
> SHELL:
> =====
> 
> sh:
> \_\_
> remote\_tmp(/etc/ansible/ansible.cfg) = /tmp
> \`\`\`
> 
> \### OS / Environment
> 
> PRETTY\_NAME="Debian GNU/Linux 12 (bookworm)"
> NAME="Debian GNU/Linux"
> VERSION\_ID="12"
> VERSION="12 (bookworm)"
> VERSION\_CODENAME=bookworm
> ID=debian
> HOME\_URL="https://www.debian.org/"
> SUPPORT\_URL="https://www.debian.org/support"
> BUG\_REPORT\_URL="https://bugs.debian.org/"
> 
> 
> \### Steps to Reproduce
> 
> 
> \`\`\`yaml (paste below)
> \- hosts: localhost
> gather\_facts: false
> tasks:
> - debug:
> msg: "{{ 'vagrant-libvirt' | password\_hash(hashtype='bcrypt') }}"
> \`\`\`
> 
> 
> \### Expected Results
> 
> \`\`\`
> PLAY \[localhost\] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
> 
> TASK \[debug\] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
> ok: \[localhost\] =\> {
> "msg": "$2b$12$OBcE8LP/V6mIm2GEnoperegQoN9/8dTUcBVsLn/X9FbD5WCUGoU3e"
> }
> 
> PLAY RECAP \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
> localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
> \`\`\`
> 
> \### Actual Results
> 
> \`\`\`console
> \[ERROR\]: Task failed: Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': The filter plugin 'ansible.builtin.password\_hash' failed: Could not hash the secret: password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my\_password\[:72\])
> 
> Task failed: Finalization of task args for 'ansible.builtin.debug' failed.
> Origin: /opt/configuration/environments/kolla/playbook-x.yml:4:7
> 
> 2 gather\_facts: false
> 3 tasks:
> 4 - debug:
> ^ column 7
> 
> \<\<\< caused by \>\>\>
> 
> Error while resolving value for 'msg': The filter plugin 'ansible.builtin.password\_hash' failed: Could not hash the secret: password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my\_password\[:72\])
> Origin: /opt/configuration/environments/kolla/playbook-x.yml:5:14
> 
> 3 tasks:
> 4 - debug:
> 5 msg: "{{ 'vagrant-libvirt' | password\_hash(hashtype='bcrypt') }}"
> ^ column 14
> 
> fatal: \[localhost\]: FAILED! =\> {"msg": "Task failed: Finalization of task args for 'ansible.builtin.debug' failed: Error while resolving value for 'msg': The filter plugin 'ansible.builtin.password\_hash' failed: Could not hash the secret: password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my\_password\[:72\])"}
> \`\`\`
> 
> \### Code of Conduct
> 
> \- \[x\] I agree to follow the Ansible Code of Conduct

Since it’s a `passlib` issue and `passlib` is apparently no longer maintained, here’s a patch:

```auto
--- passlib-1.7.4/passlib/handlers/bcrypt.orig	2025-10-06 16:28:59.171474455 -0400
+++ passlib-1.7.4/passlib/handlers/bcrypt.py	2025-10-08 10:38:00.682318159 -0400
@@ -374,14 +374,26 @@
             NOTE: if in future we need to deliberately create hashes which have this bug,
                   can use something like 'hashpw(repeat_string(secret[:((1+secret) % 256) or 1]), 72)'
             """
- # check if it exhibits wraparound bug
+
+ # Secret which will trip the wraparound bug, if present
             secret = (b"0123456789"*26)[:255]
- bug_hash = ident.encode("ascii") + b"04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"
- if verify(secret, bug_hash):
- return True
 
- # if it doesn't have wraparound bug, make sure it *does* handle things
- # correctly -- or we're in some weird third case.
+ # Python bcrypt >= 5.0.0 will raise an exception on passwords great than 72 characters,
+ # whereas earlier versions without the wraparound bug silently truncated the input to 72
+ # characters. See if the exception is generated.
+
+ try:
+ bug_hash = ident.encode("ascii") + b"04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"
+
+ # If we get here, the backend auto-truncates, test for wraparound bug
+ if verify(secret, bug_hash):
+ return True
+ except ValueError:
+ # Backend explicitly will not auto-truncate, truncate the password to 72 characters
+ secret = secret[:72]
+            
+ # Check to make sure that the backend still hashes correctly; if not, we're in a failure case
+ # not related to the original wraparound bug or bcrypt >= 5.0.0 input length restriction.
             correct_hash = ident.encode("ascii") + b"04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi"
             if not verify(secret, correct_hash):
                 raise RuntimeError("%s backend failed to verify %s wraparound hash" % (backend, ident))
@@ -617,10 +629,16 @@
         except ImportError: # pragma: no cover
             return False
         try:
- version = _bcrypt. __about__. __version__
+ # "New style" (793bef 2023-11-23) version
+ version = _bcrypt. __version__
         except:
- log.warning("(trapped) error reading bcrypt version", exc_info=True)
- version = '<unknown>'
+ try:
+ # Old style verion
+ version = _bcrypt. __about__. __version__
+ except:
+ # Can't find version
+ log.warning("(trapped) error reading bcrypt version", exc_info=True)
+ version = '<unknown>'
 
         log.debug("detected 'bcrypt' backend, version %r", version)
         return mixin_cls._finalize_backend_mixin(name, dryrun)

```

This patch fixes the underlying issue (`bcrypt` 5.0.0 no longer silently truncates too-long passwords) which was exposed in one of the `passlib` internal tests for an old wraparound bug. If the new `ValueError` is caught during the wraparound test, the test string is truncated to 72 characters. In either case, the original test for proper hash functionality still executes.

The patch also cleans up an annoying (but inconsequential) warning about `passlib` not being able to find version information on `bcrypt`, which was due to a change in where `bcrypt` stores version information from November 2023.

I’ve got the patch integrated into a SlackBuild for the Slackware `python3-passlib` package, and am trying to get it submitted to the SlackBuild’s maintainer.

---

<div class="post-metadata">

**Author:** ![markuman](https://avatars.discourse-cdn.com/v4/letter/m/7bcc69/32.png) [@markuman](https://forum.ansible.com/u/markuman)\
**Post date:** [January 23, 2026, 1:16pm UTC](https://forum.ansible.com/t/python-passlib-and-bcrypt-issue-and-patch/44640/2 "2026-01-23T13:16:28Z")

</div>

In the meantime [ansible.builtin.password\_hash not working with bcrypt\>=5.0.0 · Issue #85919 · ansible/ansible · GitHub](https://github.com/ansible/ansible/issues/85919#issuecomment-3632580620)

> this issue should be fixed by [#85970](https://github.com/ansible/ansible/pull/85970) as it bypasses the passlib/bcrypt issues by switching to libcrypt/libxcrypt

But I think it’s not released yet …
