Title: Fixing "'humanize' is not a registered tag library" on postorious
Date: 2025-10-23 14:00

At [Nos oignons](https://nos-oignons.net/%C3%80_propos/index.en.html), we're
running [Tor exit nodes](https://nos-oignons.net/Services/index.en.html), but
also a handful of services, like our website, some git repositories, some
lightweight monitoring, … but also internal and external mailing lists. For the
latter, we're using
[mailman3](https://wiki.list.org/)/[hyperkitty](https://wiki.list.org/HyperKitty)
with [schleuder](https://schleuder.org/) on top of it, making it an unholy pile
of python, ruby, gpg and email shenanigans that the sysadmin team has to
prevent from falling over.

Unfortunately, teaching sand to think was a mistake, and anything running
on a computer will invariably fail at some point, mailing lists are no
exceptions. Today I got the following email on the `schleuder-admins@` list:

```
Net::ReadTimeout with #<TCPSocket:(closed)>
/usr/lib/ruby/3.3.0/net/protocol.rb:229:in `rbuf_fill'
/usr/lib/ruby/3.3.0/net/protocol.rb:199:in `readuntil'
/usr/lib/ruby/3.3.0/net/protocol.rb:209:in `readline'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:1017:in `recv_response'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:979:in `block in data'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:1027:in `critical'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:965:in `data'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:799:in `block in send_message'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:926:in `rcptto_list'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:799:in `send_message'
/usr/lib/ruby/vendor_ruby/mail/network/delivery_methods/smtp_connection.rb:53:in `deliver!'
/usr/lib/ruby/vendor_ruby/mail/network/delivery_methods/smtp.rb:101:in `block in deliver!'
/usr/lib/ruby/gems/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:643:in `start'
/usr/lib/ruby/vendor_ruby/mail/network/delivery_methods/smtp.rb:109:in `start_smtp_session'
/usr/lib/ruby/vendor_ruby/mail/network/delivery_methods/smtp.rb:100:in `deliver!'
/usr/lib/ruby/vendor_ruby/mail/message.rb:2145:in `do_delivery'
/usr/lib/ruby/vendor_ruby/mail/message.rb:255:in `deliver'
/usr/lib/ruby/vendor_ruby/schleuder/mail/gpg/delivery_handler.rb:27:in `deliver_mail'
/usr/lib/ruby/vendor_ruby/mail/message.rb:253:in `deliver'
/usr/lib/ruby/vendor_ruby/schleuder/logger_notifications.rb:48:in `block in notify_admin'
/usr/lib/ruby/vendor_ruby/schleuder/logger_notifications.rb:29:in `each'
/usr/lib/ruby/vendor_ruby/schleuder/logger_notifications.rb:29:in `notify_admin'
/usr/lib/ruby/vendor_ruby/schleuder/logger_notifications.rb:13:in `error'
/usr/lib/ruby/vendor_ruby/schleuder/list.rb:379:in `rescue in block in send_to_subscriptions'
/usr/lib/ruby/vendor_ruby/schleuder/list.rb:362:in `block in send_to_subscriptions'
/usr/share/rubygems-integration/all/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:98:in `each'
/usr/share/rubygems-integration/all/gems/activerecord-7.2.2.1/lib/active_record/relation/delegation.rb:98:in `each'
/usr/lib/ruby/vendor_ruby/schleuder/list.rb:361:in `send_to_subscriptions'
/usr/lib/ruby/vendor_ruby/schleuder/runner.rb:90:in `run'
/usr/lib/ruby/vendor_ruby/schleuder/cli.rb:38:in `work'
/usr/share/rubygems-integration/all/gems/thor-1.3.2/lib/thor/command.rb:28:in `run'
/usr/share/rubygems-integration/all/gems/thor-1.3.2/lib/thor/invocation.rb:127:in `invoke_command'
/usr/share/rubygems-integration/all/gems/thor-1.3.2/lib/thor.rb:538:in `dispatch'
/usr/share/rubygems-integration/all/gems/thor-1.3.2/lib/thor/base.rb:584:in `start'
/usr/bin/schleuder:13:in `<main>'


[Django] ERROR (EXTERNAL IP): Internal Server Error:.eml
Subject: [Django] ERROR (EXTERNAL IP): Internal Server Error: /postorius/lists/<REDACTED>@nos-oignons.net/held_messages
From: <REDACTED>@lists.nos-oignons.net
Date: Thu, 23 Oct 2025 05:06:17 -0000
To: root@localhost, <REDACTED>@nos-oignons.net

Internal Server Error: /postorius/lists/<REDACTED>@nos-oignons.net/held_messages

TemplateSyntaxError at /postorius/lists/<REDACTED>@nos-oignons.net/held_messages
'humanize' is not a registered tag library. Must be one of:
account
admin_list
admin_modify
admin_urls
allauth
bootstrap_tags
cache
compress
d_gravatar
date_helpers
debugger_tags
decorate
gravatar
highlight
highlighting
hk_generic
hk_haystack
i18n
indent_text
l10n
log
markdown
membership_helpers
more_like_this
nav_helpers
p_gravatar
pagination
postorius_helpers
rest_framework
socialaccount
static
syntax_color
tz
widont

Request Method: GET
Request URL: https://lists.nos-oignons.net/postorius/lists/<REDACTED>@nos-oignons.net/held_messages
Django Version: 4.2.23
Python Executable: /usr/bin/uwsgi-core
Python Version: 3.13.5
Python Path: ['.', '', '/usr/lib/python313.zip', '/usr/lib/python3.13', '/usr/lib/python3.13/lib-dynload', '/usr/local/lib/python3.13/dist-packages', '/usr/lib/python3/dist-packages']
Server time: Thu, 23 Oct 2025 05:06:17 +0000
Installed Applications:
('hyperkitty',
 'postorius',
 'django_mailman3',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework',
 'django_gravatar',
 'compressor',
 'haystack',
 'django_extensions',
 'django_q',
 'allauth',
 'allauth.account',
 'allauth.socialaccount')
Installed Middleware:
('allauth.account.middleware.AccountMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django_mailman3.middleware.TimezoneMiddleware',
 'postorius.middleware.PostoriusMiddleware')


Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/django/template/defaulttags.py", line 1026, in find_library
    return parser.libraries[name]
           ^^^^^^^^^^^^^^^^^^^^^^

During handling of the above exception ('humanize'), another exception occurred:
  File "/usr/lib/python3/dist-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/contrib/auth/decorators.py", line 23, in _wrapper_view
    return view_func(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/postorius/auth/decorators.py", line 66, in wrapper
    return fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/postorius/views/list.py", line 889, in list_moderation
    return render(request, 'postorius/lists/held_messages.html', context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/shortcuts.py", line 24, in render
    content = loader.render_to_string(template_name, context, request, using=using)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/loader.py", line 61, in render_to_string
    template = get_template(template_name, using=using)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/loader.py", line 15, in get_template
    return engine.get_template(template_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/backends/django.py", line 33, in get_template
    return Template(self.engine.get_template(template_name), self)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/engine.py", line 175, in get_template
    template, origin = self.find_template(template_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/engine.py", line 157, in find_template
    template = loader.get_template(name, skip=skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/loaders/cached.py", line 57, in get_template
    template = super().get_template(template_name, skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/loaders/base.py", line 28, in get_template
    return Template(
           
  File "/usr/lib/python3/dist-packages/django/template/base.py", line 154, in __init__
    self.nodelist = self.compile_nodelist()
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/base.py", line 200, in compile_nodelist
    return parser.parse()
           ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/base.py", line 513, in parse
    raise self.error(token, e)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/base.py", line 511, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/loader_tags.py", line 293, in do_extends
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/base.py", line 513, in parse
    raise self.error(token, e)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/base.py", line 511, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/defaulttags.py", line 1088, in load
    lib = find_library(parser, name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/template/defaulttags.py", line 1028, in find_library
    raise TemplateSyntaxError(
    ^

Exception Type: TemplateSyntaxError at /postorius/lists/ag@nos-oignons.net/held_messages
Exception Value: 'humanize' is not a registered tag library. Must be one of:
account
admin_list
admin_modify
admin_urls
allauth
bootstrap_tags
cache
compress
d_gravatar
date_helpers
debugger_tags
decorate
gravatar
highlight
highlighting
hk_generic
hk_haystack
i18n
indent_text
l10n
log
markdown
membership_helpers
more_like_this
nav_helpers
p_gravatar
pagination
postorius_helpers
rest_framework
socialaccount
static
syntax_color
tz
widont
Raised during: postorius.views.list.list_moderation
```


Since this is a [django](https://www.djangoproject.com/) stacktrace complaining
about a missing application, so it should simply be a matter of
adding to `/usr/share/mailman3-web/settings.py`. Unfortunately:

```console
root@bulbe:~# grep humanize /usr/share/mailman3-web/settings.py
    'django.contrib.humanize',
root@bulbe:~#
```

After a fair amount of cursing, spelunking in [mailman-users
](https://lists.mailman3.org/archives/list/mailman-users@mailman3.org/) and
various `strace`+`grep` monstrosities, the
solution was found: Instead of adding `'django.contrib.humanize'` to the `INSTALLED_APPS` tuple
in `/usr/share/mailman3-web/settings.py` as one would expect, it should be
added in `/etc/mailman3/mailman-web.py` instead.

In the hope that this blogpost will help you waste less time on this than I
did.
