La Technique

Unattended Upgrades email notification failure

TL;DR: Curiously, the unattended-upgrades Python script in my Ubuntu Server installation encountered an ImportError towards the end of its run, and ultimately failed to send the email notification it is supposed to send. Notably, several of the system Python packages received updates during that run, including the email package in the standard library. However, I don’t understand yet the exact mechanism of the script’s failure.


Last weekend, I was surprised to see a “reboot required” message on my Ubuntu Server box after logging in through SSH. I should have received an email for this, but there was none.

I checked the Unattended Upgrades logs at /var/log/unattended-upgrades/unattended-upgrades.log (added text wrapping and newlines between log entries):

2024-09-18 06:06:55,671 INFO Starting unattended upgrades script

2024-09-18 06:06:55,672 INFO Allowed origins are: o=Ubuntu,a=jammy, 
o=Ubuntu,a=jammy-security, o=UbuntuESMApps,a=jammy-apps-security, 
o=UbuntuESM,a=jammy-infra-security, o=Ubuntu,a=jammy, o=Ubuntu,a=jammy-security, 
o=UbuntuESMApps,a=jammy-apps-security, o=UbuntuESM,a=jammy-infra-security

2024-09-18 06:06:55,672 INFO Initial blacklist:

2024-09-18 06:06:55,672 INFO Initial whitelist (not strict):

2024-09-18 06:07:19,237 INFO Packages that will be upgraded: curl 
libcurl3-gnutls libcurl4 libexpat1 libexpat1-dev libnginx-mod-http-geoip2 
libnginx-mod-http-image-filter libnginx-mod-http-xslt-filter libnginx-mod-mail 
libnginx-mod-stream libnginx-mod-stream-geoip2 libpython3.10 libpython3.10-dev 
libpython3.10-minimal libpython3.10-stdlib linux-generic linux-headers-generic 
linux-image-generic linux-libc-dev nginx nginx-common nginx-core python3.10 
python3.10-dev python3.10-minimal python3.10-venv

2024-09-18 06:07:19,238 INFO Writing dpkg log to 
/var/log/unattended-upgrades/unattended-upgrades-dpkg.log

2024-09-18 06:08:32,571 INFO All upgrades installed

2024-09-18 06:08:48,716 INFO Packages that were successfully auto-removed:

2024-09-18 06:08:48,717 INFO Packages that are kept back:

2024-09-18 06:08:49,439 ERROR An error occurred: cannot import name 
'HeaderWriteError' from 'email.errors' (/usr/lib/python3.10/email/errors.py)
Traceback (most recent call last):
File "/usr/bin/unattended-upgrade", line 2005, in main
    send_summary_mail(res.pkgs, res.success, res.result_str,
File "/usr/bin/unattended-upgrade", line 1509, in send_summary_mail
    ret = _send_mail_using_sendmail(from_email, to_email, subject, body)
File "/usr/bin/unattended-upgrade", line 1408, in _send_mail_using_sendmail
    sendmail.stdin.write(msg.as_string())
File "/usr/lib/python3.10/email/message.py", line 151, in as_string
    from email.generator import Generator
File "/usr/lib/python3.10/email/generator.py", line 17, in <module>
    from email.errors import HeaderWriteError
ImportError: cannot import name 'HeaderWriteError' from 'email.errors' 
(/usr/lib/python3.10/email/errors.py)

It turns out that the package updates that necessitated the reboot were done three days before. However, the script failed to send the email notification because it encountered a Python ImportError while trying to load a class from the standard library’s email package.

Looking at the list of packages upgraded, I spy several related to the system Python installation. I hunted down the changelog for the update to python3.10, and confirmed that the email.errors module is among the files touched by the upgrades.

I looked into the unattended-upgrade Python script itself, and found that the call to send_summary_mail() (for the email notification) is wrapped in a try/except Exception as e catch-all error handling block, so that whatever happens to the first send_summary_mail() call, the script will still get to log the issue—and then call send_summary_mail() again to make sure that the sys admin knows.

Of course, that also failed. I confirmed with the journalctl logs, sudo journalctl --since="2024-09-18 06:08:30":

Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Traceback (most recent call last):
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 2005, in main
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     send_summary_mail(res.pkgs, res.success, res.result_str,
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1509, in send_summary_mail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     ret = _send_mail_using_sendmail(from_email, to_email, subject, body)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1408, in _send_mail_using_sendmail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     sendmail.stdin.write(msg.as_string())
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/message.py", line 151, in as_string
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.generator import Generator
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/generator.py", line 17, in <module>
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.errors import HeaderWriteError
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: ImportError: cannot import name 'HeaderWriteError' from 'email.errors' (/usr/lib/python3.10/email/errors.py)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: During handling of the above exception, another exception occurred:
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Traceback (most recent call last):
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 2522, in <module>
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     sys.exit(main(options))
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 2027, in main
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     send_summary_mail(["<unknown>"], False, _("An error occurred"),
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1509, in send_summary_mail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     ret = _send_mail_using_sendmail(from_email, to_email, subject, body)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1408, in _send_mail_using_sendmail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     sendmail.stdin.write(msg.as_string())
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/message.py", line 151, in as_string
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.generator import Generator
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/generator.py", line 17, in <module>
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.errors import HeaderWriteError
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: ImportError: cannot import name 'HeaderWriteError' from 'email.errors' (/usr/lib/python3.10/email/errors.py)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Error in sys.excepthook:
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Traceback (most recent call last):
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 118, in apport_excepthook
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     pr.add_proc_info(extraenv=['PYTHONPATH', 'PYTHONHOME'])
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3/dist-packages/apport/report.py", line 634, in add_proc_info
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     raise ValueError('%s does not exist' % self['ExecutablePath'])
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: ValueError: /usr/bin/python3.10 (deleted) does not exist
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Original exception was:
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Traceback (most recent call last):
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 2005, in main
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     send_summary_mail(res.pkgs, res.success, res.result_str,
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1509, in send_summary_mail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     ret = _send_mail_using_sendmail(from_email, to_email, subject, body)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1408, in _send_mail_using_sendmail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     sendmail.stdin.write(msg.as_string())
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/message.py", line 151, in as_string
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.generator import Generator
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/generator.py", line 17, in <module>
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.errors import HeaderWriteError
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: ImportError: cannot import name 'HeaderWriteError' from 'email.errors' (/usr/lib/python3.10/email/errors.py)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: During handling of the above exception, another exception occurred:
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: Traceback (most recent call last):
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 2522, in <module>
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     sys.exit(main(options))
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 2027, in main
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     send_summary_mail(["<unknown>"], False, _("An error occurred"),
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1509, in send_summary_mail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     ret = _send_mail_using_sendmail(from_email, to_email, subject, body)
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/bin/unattended-upgrade", line 1408, in _send_mail_using_sendmail
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     sendmail.stdin.write(msg.as_string())
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/message.py", line 151, in as_string
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.generator import Generator
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:   File "/usr/lib/python3.10/email/generator.py", line 17, in <module>
Sep 18 06:08:49 gemini apt.systemd.daily[43706]:     from email.errors import HeaderWriteError
Sep 18 06:08:49 gemini apt.systemd.daily[43706]: ImportError: cannot import name 'HeaderWriteError' from 'email.errors' (/usr/lib/python3.10/email/errors.py)
Sep 18 06:08:49 gemini apt.systemd.daily[56626]: sendmail: fatal: root(0): No recipient addresses found in message header
Sep 18 06:08:49 gemini postfix/sendmail[56626]: fatal: root(0): No recipient addresses found in message header
Sep 18 06:08:49 gemini postfix/sendmail[56627]: fatal: root(0): No recipient addresses found in message header
Sep 18 06:08:49 gemini apt.systemd.daily[56627]: sendmail: fatal: root(0): No recipient addresses found in message header
Sep 18 06:08:49 gemini systemd[1]: apt-daily-upgrade.service: Deactivated successfully.
Sep 18 06:08:49 gemini systemd[1]: Finished Daily apt upgrade and clean activities.
Sep 18 06:08:49 gemini systemd[1]: apt-daily-upgrade.service: Consumed 1min 51.875s CPU time.

A minor note here: I run Postfix as an SMTP relay host to Amazon SES, so that anything that relies on sendmail working in the system environment gets to, well, send mail. Postfix has nice built-in retry mechanisms that make email sending more reliable (e.g., can overcome temporary network disconnections), but the logs show that the upgrade script did not even get a chance to generate a well-formed email object.

Of much more interest is the log entry that says this:

/usr/bin/python3.10 (deleted) does not exist

So, it appears that in the course of running the script and triggering the system package updates, the script, or the process running the script, at one point was somehow deprived of access to Python itself, or the Python libraries that were updated. At this point, I have also reached the edges of my extremely limited knowledge on Unix/Linux filesystems, Ubuntu/Debian packaging, Python/CPython module loading, or whatever mechanism or combination of mechanisms led to the error.

I know it’s not a severe error, because even if it occurred in a much more important server, there should be minimal impact because the upgrades themselves were installed successfully; it was just the post-install reboot-required notification that failed, and I think some delay with rebooting is not critical. But it would be fascinating to learn what caused it: anyone who has an idea, please drop me an email or perhaps a holler at Mastodon.

Previous: