Skip to content

Worker misparses positional arguments when a preceding parameter lacks a type annotation #666

Description

@juanmicl

Taskiq version

Taskiq version 0.12.6 (master)

Python version

Python 3.12

OS

Linux

What happened?

parse_params in taskiq/receiver/params_parser.py maps positional arguments to annotations with a counter (argnum) that only increments for annotated parameters. Unannotated parameters are skipped with continue before argnum += 1, but get_type_hints() omits them too, so they still occupy a slot in message.args. When any positional parameter has no annotation, every argument after it is parsed against the wrong annotation.

Example:

@broker.task
async def my_task(request_id, count: int) -> None: ...

await my_task.kiq("12345", 3)

What the worker receives (verified on master, taskiq 0.12.6):

  1. request_id arrives as the int 12345. The unannotated parameter's value is coerced by the next parameter's annotation.
  2. count is never validated. kiq("hello", "not-an-int") delivers "not-an-int" to count, and the only log line is a Can't parse argument 0 warning about the other parameter.
  3. If the first value fails to parse, the annotated parameter after it never gets coerced either. It keeps its wire representation, so a datetime parameter receives an ISO string.
  4. *values: int varargs coerce only the first element. kiq("1", "2", "3") arrives as [1, "2", "3"].

Standalone repro of the worker path, no broker needed:

import inspect
from typing import get_type_hints

from taskiq.formatters.json_formatter import JSONFormatter
from taskiq.message import TaskiqMessage
from taskiq.receiver.params_parser import parse_params

fmt = JSONFormatter()

def my_task(request_id, count: int) -> None: ...

msg = fmt.loads(fmt.dumps(TaskiqMessage(
    task_id="1",
    task_name="mod:my_task",
    labels={},
    labels_types={},
    args=["12345", 3],
    kwargs={},
)).message)
parse_params(inspect.signature(my_task), get_type_hints(my_task), msg)
print(msg.args)  # [12345, 3] - request_id silently became an int

Each positional argument should be parsed against the annotation of the parameter in the same position, and unannotated parameters should be left untouched. Mixing untyped and typed positional parameters (a context or config object as the first argument is a common case) hits this without any error.

Relevant log output

With `kiq("hello", "not-an-int")` the worker logs a warning for argument 0, the unannotated parameter, and nothing for `count`:


Can't parse argument 0 for task mod:my_task. Reason: 1 validation error for int
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='hello', input_type=str]

Broker initialization code

Not needed. The repro exercises the receiver's parsing path directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions