"thread already started" exception when running the worker with Python 3.13

With Python 3.13, running the worker from the CLI raise a thread already started RuntimeError:

$ env FLASK_APP=canaille:create_app flask worker --processes 1 --threads 1
[2025-03-27 14:55:04,142] -  - INFO in flask_dramatiq: Able to execute the following actors:
[2025-03-27 14:55:04,167] [PID 239952] [MainThread] [dramatiq.MainProcess] [INFO] Dramatiq '1.17.1' is booting up.
[2025-03-27 14:55:04,150] [PID 239959] [MainThread] [dramatiq.broker.RedisBroker] [CRITICAL] Unexpected failure in after_process_boot of <dramatiq.middleware.time_limit.TimeLimit object at 0x758d6b044c20>.
Traceback (most recent call last):
  File "/home/eloi/dev/yaal/canaille/.venv/lib/python3.13/site-packages/dramatiq/broker.py", line 115, in emit_after
    getattr(middleware, signal)(self, *args, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/eloi/dev/yaal/canaille/.venv/lib/python3.13/site-packages/dramatiq/middleware/time_limit.py", line 72, in after_process_boot
    self.manager.start()
    ~~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/threading.py", line 973, in start
    _start_joinable_thread(self._bootstrap, handle=self._handle,
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                           daemon=self.daemon)
                           ^^^^^^^^^^^^^^^^^^^
RuntimeError: thread already started
  • with Python3.12 and lower version, the exception is not raised. I am not sure what happened in Python 3.13 that would explain the exception.
  • it happens with RedisBroker and RabbitmqBroker
  • I could not reproduce the error by running the worker directly with dramatiq with dramatiq --processes 1 --threads 1 --path . --worker-shutdown-timeout 600000 flask_dramatiq, this is why I open the bug report here. In the end we might found that the real fix must be achieved in dramatiq directly.