
Thanks to Mark Sapiro and others who have answered questions on this list. It has made my job easier. I'm hoping someone can help with another issue I'm seeing.
I'm setting up a new mailman 3 server and doing some test posts to a list. This server is intended only as an announcement list with limited members allowed to post. The resulting emails coming from the list do not include the List-Unsubscribe header although other "List-XXX" headers are included. I'm trying to figure out why the header is missing. It seems clear from the code that the header should be included.
Here is partial text of the email source as received from the list. I've tried viewing in Outlook.com, Thunderbird, and even captured the email text at postfix prior to sending to look for the missing header. My domain has been replaced with "example.com" throughout. This is from the postfix capture
... To: "test1@example.com" <test1@example.com> Thread-Topic: Post 8 test Thread-Index: AQHb8oBBzPBOYgrDxUOIiAxhvUzthA== Date: Fri, 11 Jul 2025 16:24:38 +0000 Accept-Language: en-US Content-Language: en-US msip_labels: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=example.com; MIME-Version: 1.0 Message-ID-Hash: FWHZCRE4EA6IFP4BZTPIF23DKAKVRMDT X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Content-Filtered-By: Mailman/MimeDel 3.3.10 From: "Somewhere1, SW" <test1@example.com> X-Mailman-Version: 3.3.10 Precedence: list Reply-To: no-reply@example.com Subject: [Test1] Post 8 test List-Id: "Somewhere1, SW" <test1.example.com> List-Help: <mailto:test1-request@example.com?subject=help> List-Owner: <mailto:test1-owner@example.com> List-Post: NO Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Message-Id: <20250711163343.017C31DBB79@example.com>
Body text ...
Details on my test setup: # Mailman version. __version__ = '3.3.10' OS: Amazon Linux 2023 (RHEL 9 compatible) MTA: postfix-3.7.2
I am using the following code with mailman shell to configure the list. include_rfc2369_headers is left at the default value of True (verified via mailman shell)
# Description value must be passed as a parameter def configure_list(ml, description): #ml.accept_these_nonmembers = allowed_to_post ml.admin_immed_notify = False ml.advertised = False ml.allow_list_posts = False ml.anonymous_list = True ml.archive_policy = ArchivePolicy.never ml.bounce_info_stale_after = datetime.timedelta(days=90) ml.bounce_notify_owner_on_disable = False ml.bounce_notify_owner_on_removal = False ml.bounce_score_threshold = 3 ml.bounce_you_are_disabled_warnings = 0 ml.default_member_action = Action.discard ml.default_nonmember_action = Action.discard ml.description = description ml.digests_enabled = False ml.filter_content = True ml.first_strip_reply_to = True ml.forward_auto_discards = False ml.max_days_to_hold = 1 ml.max_message_size = 0 ml.nntp_prefix_subject_too = False ml.pass_extensions = [] ml.pass_types = ['text/plain','multipart/alternative'] ml.reply_goes_to_list = ReplyToMunging.explicit_header ml.reply_to_address = "no-reply@example.com" ml.require_explicit_destination = False ml.respond_to_post_requests = False
# Allow select members to post to list for mbr in ml.members.members: if str(mbr.address) in allowed_to_post: mbr.moderation_action = Action.defer print('end',mbr.address,mbr.moderation_action)
/etc/mailman3/mailman.cfg file:
# /etc/mailman3/mailman.cfg
# These settings override the defaults in the following 2 config files # /opt/mailman/lib/python3.9/site-packages/mailman/config/schema.cfg # /opt/mailman/lib/python3.9/site-packages/mailman/config/mailman.cfg
[paths.custom] var_dir: /data/lib/mailman pid_file: /data/lib/mailman/pid/master.pid lock_file: /data/lib/mailman/pid/master.lock
[mailman] layout: custom # This address is the "site owner" address. Certain messages which must be # delivered to a human, but which can't be delivered to a list owner (e.g. a # bounce from a list owner), will be sent to this address. It should point to # a human. site_owner: ml-owner@example.com
[database] class: mailman.database.postgresql.PostgreSQLDatabase url: postgresql:///mailman
# No archiving enabled [archiver.prototype] enable: no [archiver.hyperkitty] enable: no
[shell] history_file: $var_dir/history.py
[mta] verp_confirmations: yes verp_personalized_deliveries: yes verp_delivery_interval: 1
[passwords] # When Mailman generates them, this is the default length of passwords. password_length: 15
[logging.debug] path: debug.log level: debug
With debug enabled I see the rfc-2369 pipeline being run when processing the post to the list.
Jul 11 17:51:32 2025 (444088) [IncomingRunner] starting oneloop Jul 11 17:51:32 2025 (444088) [IncomingRunner] processing filebase: 1752256291.9136803+bfd305a28151ce750dde5114b413b351fa4d1284 Jul 11 17:51:32 2025 (444088) [IncomingRunner] processing onefile Jul 11 17:51:32 2025 (444090) [OutgoingRunner] starting oneloop Jul 11 17:51:32 2025 (444090) [OutgoingRunner] ending oneloop: 0 Jul 11 17:51:32 2025 (444097) [DigestRunner] starting oneloop Jul 11 17:51:32 2025 (444097) [DigestRunner] ending oneloop: 0 Jul 11 17:51:32 2025 (444088) [IncomingRunner] finishing filebase: 1752256291.9136803+bfd305a28151ce750dde5114b413b351fa4d1284 Jul 11 17:51:32 2025 (444088) [IncomingRunner] doing periodic Jul 11 17:51:32 2025 (444088) [IncomingRunner] committing transaction Jul 11 17:51:32 2025 (444088) [IncomingRunner] checking short circuit Jul 11 17:51:32 2025 (444088) [IncomingRunner] ending oneloop: 1 Jul 11 17:51:32 2025 (444088) [IncomingRunner] starting oneloop Jul 11 17:51:32 2025 (444088) [IncomingRunner] ending oneloop: 0 Jul 11 17:51:32 2025 (444087) [CommandRunner] starting oneloop Jul 11 17:51:32 2025 (444087) [CommandRunner] ending oneloop: 0 Jul 11 17:51:32 2025 (444091) [PipelineRunner] starting oneloop Jul 11 17:51:32 2025 (444091) [PipelineRunner] processing filebase: 1752256292.373131+a372784a3ff7a76a662ed1c62c224a53a8008b8b Jul 11 17:51:32 2025 (444091) [PipelineRunner] processing onefile Jul 11 17:51:32 2025 (444096) [VirginRunner] starting oneloop Jul 11 17:51:32 2025 (444096) [VirginRunner] ending oneloop: 0 Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: validate-authenticity Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: mime-delete Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: tagger Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: member-recipients Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: avoid-duplicates Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: cleanse Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: cleanse-dkim Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: cook-headers Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: subject-prefix Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: rfc-2369 Jul 11 17:51:32 2025 (444091) RFC 2369 process Jul 11 17:51:32 2025 (444091) RFC 2369 process continue 1 Jul 11 17:51:32 2025 (444091) RFC 2369 process headers extend Jul 11 17:51:32 2025 (444091) RFC 2369 process header for loop Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: to-archive Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: to-digest Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: to-usenet Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: after-delivery Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: acknowledge Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: dmarc Jul 11 17:51:32 2025 (444091) <20250711175131.B8CEC1DA51B@example.com> pipeline default-posting-pipeline processing: to-outgoing Jul 11 17:51:32 2025 (444091) [PipelineRunner] finishing filebase: 1752256292.373131+a372784a3ff7a76a662ed1c62c224a53a8008b8b Jul 11 17:51:32 2025 (444091) [PipelineRunner] doing periodic Jul 11 17:51:32 2025 (444091) [PipelineRunner] committing transaction Jul 11 17:51:32 2025 (444091) [PipelineRunner] checking short circuit Jul 11 17:51:32 2025 (444091) [PipelineRunner] ending oneloop: 1 Jul 11 17:51:32 2025 (444091) [PipelineRunner] starting oneloop Jul 11 17:51:32 2025 (444091) [PipelineRunner] ending oneloop: 0 Jul 11 17:51:32 2025 (444085) [ArchiveRunner] starting oneloop Jul 11 17:51:32 2025 (444085) [ArchiveRunner] ending oneloop: 0 Jul 11 17:51:33 2025 (444090) [OutgoingRunner] starting oneloop Jul 11 17:51:33 2025 (444090) [OutgoingRunner] processing filebase: 1752256292.8885148+5e5c4a68eacfead13561df616fe7ca19e1d1d182 Jul 11 17:51:33 2025 (444090) [OutgoingRunner] processing onefile Jul 11 17:51:33 2025 (444097) [DigestRunner] starting oneloop Jul 11 17:51:33 2025 (444097) [DigestRunner] ending oneloop: 0 Jul 11 17:51:33 2025 (444090) [outgoing] <function deliver at 0x7f21ed7b1310>: <175225629286.444091.14942604217243398224@ip-10-0-0-168.us-gov-west-1.compute.internal> Jul 11 17:51:33 2025 (444088) [IncomingRunner] starting oneloop
I tried inserting some log.debug calls into lib/python3.9/site-packages/mailman/handlers/rfc_2369.py to see where headers were being removed and added. I modified: log = logging.getLogger('mailman.debug') and inserted log.debug throughout the process function and one in the top level.
...
@public @implementer(IHandler) class RFC2369: """Add the RFC 2369 List-* headers."""
name = 'rfc-2369'
description = _('Add the RFC 2369 List-* headers.')
log.debug('RFC 2369 processing')
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
process(mlist, msg, msgdata)
When running with debug enabled, I see this top level message many times at the top of the debug.log file during mailman startup, but not after that. I don't see any of my other debug messages.
Jul 11 16:03:36 2025 (409226) RFC 2369 processing Jul 11 16:03:40 2025 (409244) RFC 2369 processing Jul 11 16:03:41 2025 (409250) RFC 2369 processing Jul 11 16:03:49 2025 (409271) RFC 2369 processing Jul 11 16:03:50 2025 (409271) [DigestRunner] starting oneloop Jul 11 16:03:50 2025 (409271) [DigestRunner] ending oneloop: 0 Jul 11 16:03:50 2025 (409261) RFC 2369 processing Jul 11 16:03:50 2025 (409261) [BounceRunner] starting oneloop Jul 11 16:03:50 2025 (409261) [BounceRunner] ending oneloop: 0 Jul 11 16:03:50 2025 (409264) RFC 2369 processing Jul 11 16:03:50 2025 (409267) RFC 2369 processing Jul 11 16:03:51 2025 (409262) RFC 2369 processing Jul 11 16:03:51 2025 (409262) [CommandRunner] starting oneloop Jul 11 16:03:51 2025 (409262) [CommandRunner] ending oneloop: 0 Jul 11 16:03:51 2025 (409266) RFC 2369 processing Jul 11 16:03:51 2025 (409266) [PipelineRunner] starting oneloop Jul 11 16:03:51 2025 (409266) [PipelineRunner] ending oneloop: 0 ...
Any suggestions on where else I can look? Any thoughts on why my debug calls are not showing in the logs?