After running brew upgrade on my Mac M3, OfflineIMAP suddenly stopped
working with this error:
OfflineIMAP 8.0.1
Licensed under the GNU GPL v2 or any later version (with an OpenSSL exception)
imaplib2 v3.06, Python v3.14.0, OpenSSL 3.6.0 1 Oct 2025
Account sync Peter:
*** Processing account Peter
Establishing connection to imap.mailbox.org:993 (PeterRemote)
ERROR: While attempting to sync account 'Peter'
No module named 'keyring'
*** Finished account 'Peter' in 0:00
ERROR: Exceptions occurred during the run!
ERROR: While attempting to sync account 'Peter'
ModuleNotFoundError: No module named 'keyring'
For context, OfflineIMAP had been working perfectly for a long time. I never needed any keyring module - it would simply prompt me for passwords when needed. The brew upgrade likely updated either OfflineIMAP or Python 3.14, breaking this dependency.
Understanding the Root Cause#
The issue occurs because:
- Homebrew upgraded OfflineIMAP or Python to a newer version
- The new version has an implicit dependency on the
keyringPython module - Homebrew's OfflineIMAP formula doesn't declare
keyringas a dependency - The module is missing from the Python environment that OfflineIMAP uses
When I checked which Python OfflineIMAP was using:
which offlineimap
head -1 (which offlineimap)
It pointed to /opt/homebrew/opt/[email protected] - Homebrew's managed Python
installation.
System-wide Installation#
There are two ways to install the missing keyring module - the first is
system-wide:
/opt/homebrew/opt/[email protected]/bin/pip3 install keyring --break-system-packages
This installs keyring directly into Homebrew's Python 3.14 site-packages
directory at /opt/homebrew/lib/python3.14/site-packages/.
Collecting keyring
Downloading keyring-25.7.0-py3-none-any.whl.metadata (21 kB)
Collecting jaraco.classes (from keyring)
Downloading jaraco.classes-3.4.0-py3-none-any.whl.metadata (2.9 kB)
...
Successfully installed jaraco.classes-3.4.0 jaraco.context-6.0.1 jaraco.functools-4.1.0 keyring-25.7.0 more-itertools-10.5.0
The second option is an user installation by adding --user switch:
/opt/homebrew/opt/[email protected]/bin/pip3 install keyring --break-system-packages --user
This installs keyring into your user's local Python packages directory at
~/.local/lib/python3.14/site-packages/.
For single-user Mac systems (most common case), Option 1 is simpler and more straightforward. For multi-user systems or if you want packages to survive Python upgrades, Option 2 is better.
Understanding --break-system-packages#
The --break-system-packages flag is required because of PEP 668, which
prevents pip from modifying system-managed Python installations. Modern
Python distributions (including Homebrew's) are marked as "externally
managed" to prevent conflicts between pip and the system package manager.
While the flag name sounds dangerous, on macOS with Homebrew it's relatively safe because:
- Homebrew's Python is isolated from macOS system Python
(
/usr/bin/python3) - Breaking Homebrew's Python won't affect macOS system tools
- You can always recover with
brew reinstall [email protected]
Verification#
After installing keyring, OfflineIMAP works perfectly:
offlineimap
The error is gone, and email synchronization proceeds normally.
Why This Happened#
Looking at Homebrew's OfflineIMAP formula, the keyring module is not
listed as a resource dependency:
resource "distro" do
url "https://files.pythonhosted.org/packages/.../distro-1.9.0.tar.gz"
sha256 "2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"
end
resource "imaplib2" do
# ...
end
resource "rfc6555" do
# ...
end
resource "urllib3" do
# ...
end
# keyring is missing!
This appears to be an oversight in the Homebrew formula. The proper fix would be to submit a pull request to homebrew-core adding keyring and its dependencies (jaraco.classes, jaraco.context, jaraco.functools, more-itertools) to the formula.
Conclusion#
The --break-system-packages approach is a pragmatic fix for this
dependency gap in Homebrew's OfflineIMAP formula. While it's not the most
elegant solution, it works reliably and can be easily repeated if needed
after Python upgrades. Enjoy!