You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Issue starting worker with PrometheusMiddleware #173
Hi, thanks for the awesome library. I am trying to add the PrometheusMiddleware to an application, however, I am seeing some strange behavior when starting the worker(s).
taskiq version: 0.8.2 (although I observed the same behavior on 0.8.1) prometheus-client version: 0.17.1 (although I observed the same behavior on 0.16.0)
When I remove the .with_middlewares call with the broker I am able to start the worker (and process tasks) with two workers. However, what is interesting is that when I reduce it to one worker I see the same output as one worker with prometheus middleware enabled: FileNotFoundError
Any ideas? I am relatively new to python so kind of scratching my head at what else could be useful to debug, please let me know if you need any additional info! Thanks in advance!
@s3rius thanks for the fix, I was debugging the middleware a bit today and I noticed that the startup callback wasn't being executed.
I read the prometheus client documentation's section on multiprocessing and I noticed a couple of things. I implemented some changes which made the metrics start working with two workers. I still see the issue with one worker, not sure what that's about 🤷♂️
I opened #176 with my changes. Please take a look and let me know what you think! Again, I am not much of a python dev so lmk if anything looks off. Thanks again for the great work here, I learned a lot reading this codebase.
@s3rius yes I still get the error on 0.8.3 -- the only way I've been able to resolve the Duplicated timeseries in CollectorRegistry: {'found_errors_total', 'found_errors', 'found_errors_created'} error is with my fork
I'm still experiencing this issue on 0.12.3 with 4 workers. Here is my logs:
+ exec taskiq worker -fsd app.core.taskiq:taskiq_broker --workers 4 --max-fails 1 --wait-tasks-timeout 5
[2026-05-07 17:19:16,777][taskiq.worker][INFO ][MainProcess] Pid of a main process: 1860663
[2026-05-07 17:19:16,777][taskiq.worker][INFO ][MainProcess] Starting 4 worker processes.
[2026-05-07 17:19:17,288][taskiq.process-manager][INFO ][MainProcess] Started process worker-0 with pid 1860704
[2026-05-07 17:19:17,291][taskiq.process-manager][INFO ][MainProcess] Started process worker-1 with pid 1860705
[2026-05-07 17:19:17,294][taskiq.process-manager][INFO ][MainProcess] Started process worker-2 with pid 1860706
[2026-05-07 17:19:17,297][taskiq.process-manager][INFO ][MainProcess] Started process worker-3 with pid 1860707
Process worker-1:
Traceback (most recent call last):
File "/nix/store/ywlq18m07p4sq73k4xxmn1p72h8pjgdl-python3-3.14.3-env/lib/python3.14/multiprocessing/process.py", line
320, in _bootstrap
self.run()
~~~~~~~~^^
File "/nix/store/ywlq18m07p4sq73k4xxmn1p72h8pjgdl-python3-3.14.3-env/lib/python3.14/multiprocessing/process.py", line
108, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/tas
kiq/cli/worker/run.py", line 176, in start_listen
loop.run_until_complete(receiver.listen(shutdown_event))
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "uvloop/loop.pyx", line 1518, in uvloop.loop.Loop.run_until_complete
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/tas
kiq/receiver/receiver.py", line 375, in listen
await self.broker.startup()
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/tas
kiq_redis/redis_broker.py", line 239, in startup
await super().startup()
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/tas
kiq/abc/broker.py", line 198, in startup
await maybe_awaitable(middleware.startup())
~~~~~~~~~~~~~~~~~~^^
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/tas
kiq/middlewares/prometheus_middleware.py", line 94, in startup
MultiProcessCollector(REGISTRY)
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/pro
metheus_client/multiprocess.py", line 33, in __init__
registry.register(self)
~~~~~~~~~~~~~~~~~^^^^^^
File "/home/danie/src/imespro.bitbucket.org/imespro_ai/mes-portal/.devenv/state/venv/lib/python3.14/site-packages/pro
metheus_client/registry.py", line 43, in register
raise ValueError(
'Duplicated timeseries in CollectorRegistry: {}'.format(
duplicates))
ValueError: Duplicated timeseries in CollectorRegistry: {'received_tasks_total', 'execution_time_created', 'execution_t
ime_sum', 'saved_results', 'saved_results_total', 'found_errors_total', 'execution_time_count', 'found_errors_created',
'success_tasks', 'saved_results_created', 'received_tasks_created', 'received_tasks', 'success_tasks_created', 'succes
s_tasks_total', 'execution_time', 'found_errors', 'execution_time_bucket'}
We hit Duplicated timeseries again on 0.13.0 and tracked down the cause in our setup. It is also the root cause of #397, so sharing it here in case others run into it.
Cause: --fs-discover imports the broker module a second time under a different module name.
import_tasks derives module names from the file paths relative to the current working directory (str(path.with_name(prefix)).replace(os.path.sep, ".")). With a src/ layout and PYTHONPATH=src, running
from the project root imports src/worker/briefings/task_x.py as src.worker.briefings.task_x. Its from ..broker import broker then resolves to src.worker.broker, which is a second copy of worker.broker. Executing that module again constructs a second PrometheusMiddleware, and its __init__ registers found_errors, received_tasks, ... on the global REGISTRY a second time:
File "src/worker/briefings/task_briefing_autoconfirm.py", line 9, in <module>
from ..broker import broker
File "src/worker/broker.py", line 88, in <module>
PrometheusMiddleware(server_addr="0.0.0.0", server_port=9000),
File ".../taskiq/middlewares/prometheus_middleware.py", line 52, in __init__
self.found_errors = Counter(
prometheus_client.registry.DuplicateTimeseries: Duplicated timeseries in CollectorRegistry: {'found_errors_total', 'found_errors_created', 'found_errors'}
Without the middleware this goes unnoticed: you silently end up with two broker instances, each with all tasks registered (sys.modules contains both worker.broker and src.worker.broker), and it keeps working because tasks are also looked up in the global registry.
Fix on our side: drop --fs-discover (our worker/__init__.py already imports every task module), or run the worker from a directory where the discovered module names match the real import path. #682 (making the middleware import-safe) would avoid the crash as well.
Note that the traceback in the previous comment (raised from startup() → MultiProcessCollector(REGISTRY)) is a different problem that #620 already fixed: since then startup() uses a dedicated CollectorRegistry.
One more thing we noticed with --workers > 1: __init__ does if not metrics_path.exists(): metrics_path.mkdir(parents=True) without exist_ok=True. Worker processes starting at the same time race on it, and the loser dies with FileExistsError until the process manager restarts it. Pre-creating the directory works around it; mkdir(parents=True, exist_ok=True) would fix it in the middleware.
Activity
nickdichev-firework commentedon Jul 14, 2023
@s3rius thanks for the fix, I was debugging the middleware a bit today and I noticed that the
startupcallback wasn't being executed.I read the prometheus client documentation's section on multiprocessing and I noticed a couple of things. I implemented some changes which made the metrics start working with two workers. I still see the issue with one worker, not sure what that's about 🤷♂️
I opened #176 with my changes. Please take a look and let me know what you think! Again, I am not much of a python dev so lmk if anything looks off. Thanks again for the great work here, I learned a lot reading this codebase.
s3rius commentedon Jul 14, 2023
@nickdichev-firework Did you update you taskiq?
s3rius commentedon Jul 14, 2023
Yesterday I made changes. Today I published a new release. Please try version https://github.com/taskiq-python/taskiq/releases/tag/0.8.3.
The problem with registries is that it shows only taskiq's metrics. If user wants to add new metrics, they won't be shown by taskiq.
nickdichev-firework commentedon Jul 14, 2023
@s3rius yes I still get the error on 0.8.3 -- the only way I've been able to resolve the
Duplicated timeseries in CollectorRegistry: {'found_errors_total', 'found_errors', 'found_errors_created'}error is with my forkdanielphan2003 commentedon May 7, 2026
I'm still experiencing this issue on 0.12.3 with 4 workers. Here is my logs:
s3rius commentedon May 8, 2026
Huh. That's interesting. Seems like this is a regression.
@danielphan2003, can you please share you setup so I can reproduce it? Could it be that you manually create some metrics that are used by taskiq?
waza-ari commentedon Oct 13, 2026
We hit
Duplicated timeseriesagain on 0.13.0 and tracked down the cause in our setup. It is also the root cause of #397, so sharing it here in case others run into it.Cause:
--fs-discoverimports the broker module a second time under a different module name.import_tasksderives module names from the file paths relative to the current working directory (str(path.with_name(prefix)).replace(os.path.sep, ".")). With asrc/layout andPYTHONPATH=src, runningfrom the project root imports
src/worker/briefings/task_x.pyassrc.worker.briefings.task_x. Itsfrom ..broker import brokerthen resolves tosrc.worker.broker, which is a second copy ofworker.broker. Executing that module again constructs a secondPrometheusMiddleware, and its__init__registersfound_errors,received_tasks, ... on the globalREGISTRYa second time:Without the middleware this goes unnoticed: you silently end up with two broker instances, each with all tasks registered (
sys.modulescontains bothworker.brokerandsrc.worker.broker), and it keeps working because tasks are also looked up in the global registry.Fix on our side: drop
--fs-discover(ourworker/__init__.pyalready imports every task module), or run the worker from a directory where the discovered module names match the real import path. #682 (making the middleware import-safe) would avoid the crash as well.Note that the traceback in the previous comment (raised from
startup()→MultiProcessCollector(REGISTRY)) is a different problem that #620 already fixed: since thenstartup()uses a dedicatedCollectorRegistry.One more thing we noticed with
--workers > 1:__init__doesif not metrics_path.exists(): metrics_path.mkdir(parents=True)withoutexist_ok=True. Worker processes starting at the same time race on it, and the loser dies withFileExistsErroruntil the process manager restarts it. Pre-creating the directory works around it;mkdir(parents=True, exist_ok=True)would fix it in the middleware.