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):
request_id arrives as the int 12345. The unannotated parameter's value is coerced by the next parameter's annotation.
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.
- 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.
*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.
Taskiq version
Taskiq version
0.12.6 (master)Python version
Python 3.12
OS
Linux
What happened?
parse_paramsintaskiq/receiver/params_parser.pymaps positional arguments to annotations with a counter (argnum) that only increments for annotated parameters. Unannotated parameters are skipped withcontinuebeforeargnum += 1, butget_type_hints()omits them too, so they still occupy a slot inmessage.args. When any positional parameter has no annotation, every argument after it is parsed against the wrong annotation.Example:
What the worker receives (verified on master, taskiq 0.12.6):
request_idarrives as theint12345. The unannotated parameter's value is coerced by the next parameter's annotation.countis never validated.kiq("hello", "not-an-int")delivers"not-an-int"tocount, and the only log line is aCan't parse argument 0warning about the other parameter.datetimeparameter receives an ISO string.*values: intvarargs coerce only the first element.kiq("1", "2", "3")arrives as[1, "2", "3"].Standalone repro of the worker path, no broker needed:
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
Broker initialization code