Skip to content

[BUG] Float delay values cause crash (ValueError) because delay label is not deserialized #17

@omil-work

Description

@omil-work

taskiq-postgresql version

0.4.0

Python version

Python 3.14

PostgreSQL driver

asyncpg

PostgreSQL version

Debian 18.1-1.pgdg13+2

Summary

taskiq-postgresql crashes when a task has a float delay, because the broker expects the delay label to be an int.
However, both:

  • SmartRetryMiddleware
  • and explicit task definitions like @broker.task(delay=2.5)

produce float delays, which are serialized as strings (e.g., "2.5") before being stored.

During processing, taskiq-postgresql attempts to parse this value using:

delay_value = message.labels.get("delay")
if delay_value is not None:
delay_seconds = int(delay_value)

This fails for any non-integer delay and causes the worker to crash.


Steps to Reproduce

  1. Configure a PostgreSQL broker.
  2. Register a task with a float delay label (delay=2.5)
  3. Execute the task

Minimal code example

import asyncio
from taskiq_postgresql import PostgresqlBroker, PostgresqlResultBackend
from taskiq.api import run_receiver_task

async def main() -> None:
    backend = PostgresqlResultBackend(
        dsn='dsn',
        run_migrations=True,
    )

    broker = PostgresqlBroker(
        dsn='dsn',
        run_migrations=True,
    ).with_result_backend(backend)

    await broker.startup()
    
    worker_task = asyncio.create_task(run_receiver_task(broker))

    async def handler(x):
        print("A", x)

    dyn_task = broker.register_task(
        handler,
        task_name="dyn_task",
        delay=2.5,
    )

    await dyn_task.kiq(x=4)
    await asyncio.sleep(5)

    worker_task.cancel()

    try:
        await worker_task
    except asyncio.CancelledError:
        print("Worker successfully exited.")

    await broker.shutdown()


if __name__ == "__main__":
    asyncio.run(main())

Actual Behavior

Worker fails and freezes with:

delay_seconds = int(delay_value)
ValueError: invalid literal for int() with base 10: '2.5'

because the label value arrives as a serialized string "2.5".


Expected Behavior

  • Delay labels should be properly deserialized before parsing.
  • Float delays (commonly produced by SmartRetryMiddleware) should be supported.
  • Worker should not crash.

Root Cause

  • taskiq serializes label values, turning 2.5 into "2.5".

  • taskiq-postgresql does not deserialize the label before using it.

  • The broker assumes delay is always an integer, but this is not true when:

    • using @broker.task(delay=2.5)
    • using SmartRetryMiddleware, which computes delays as floats (e.g., exponential backoff)

This causes int("2.5") to raise a ValueError.

Proposed Fix

Deserialize the label value by parsing it like its done in taskiq-aio-pika:
https://github.com/taskiq-python/taskiq-aio-pika/blob/ed4a0f9904466d175b515ff4a1efbc6be5c21da9/taskiq_aio_pika/broker.py#L262

Or simply change int to float like so:

delay_value = message.labels.get("delay")
if delay_value is not None:
    delay_seconds = float(delay_value)

Additionally, _schedule_notification will need its delay_seconds parameter changed from int to float.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions