{"record":{"id":"2083fb8509b09f66","repo":"python/cpython","slug":"wrong-field-count-in-line-r","errorCode":null,"errorMessage":"Wrong field count in {line!r}","messagePattern":"Wrong field count in (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Doc/tools/extensions/c_annotations.py","lineNumber":94,"sourceCode":"    # Defines how much of the struct is exposed. Only relevant for structs.\n    # Source: [<item_kind>.*.struct_abi_kind] in stable_abi.toml.\n    struct_abi_kind: str\n\n\ndef read_refcount_data(refcount_filename: Path) -> dict[str, RefCountEntry]:\n    refcount_data = {}\n    refcounts = refcount_filename.read_text(encoding=\"utf8\")\n    for line in refcounts.splitlines():\n        line = line.strip()\n        if not line or line.startswith(\"#\"):\n            # blank lines and comments\n            continue\n\n        # Each line is of the form\n        # function ':' type ':' [param name] ':' [refcount effect] ':' [comment]\n        parts = line.split(\":\", 4)\n        if len(parts) != 5:\n            raise ValueError(f\"Wrong field count in {line!r}\")\n        function, type, arg, refcount, _comment = parts\n\n        # Get the entry, creating it if needed:\n        try:\n            entry = refcount_data[function]\n        except KeyError:\n            entry = refcount_data[function] = RefCountEntry(function)\n        if not refcount or refcount == \"null\":\n            refcount = None\n        else:\n            refcount = int(refcount)\n        # Update the entry with the new parameter\n        # or the result information.\n        if arg:\n            entry.args.append((arg, type, refcount))\n        else:\n            entry.result_type = type\n            entry.result_refs = refcount","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Doc/tools/extensions/c_annotations.py#L76-L112","documentation":" Raised by asyncio queues when task_done() is called more times than items were put(). Every put() increments _unfinished_tasks and every task_done() decrements it; the count may not go below zero. The error means the application's get/task_done bookkeeping is unbalanced — typically task_done() called without a successful get(), or from multiple consumers for one item.","triggerScenarios":"Calling q.task_done() without a preceding q.get(); calling task_done() twice per item; a worker that calls task_done() in both a normal branch and an exception/finally branch for the same item; calling task_done() after get_nowait() on an item already accounted for.","commonSituations":"Worker-pool patterns with try/finally where task_done() is also called in the except path; refactoring a single-consumer queue to multiple consumers without adjusting bookkeeping; retry logic that re-processes an item and calls task_done() again; mixing join() coordination with manual deque operations.","solutions":["Call task_done() exactly once per successfully gotten item — audit all call sites","Use the canonical worker shape: item = await q.get(); try: process(item) finally: q.task_done()","Remove duplicate task_done() calls in exception handlers or retries; if you re-queue on failure, do it via put() so the counter rises again","If you don't need join(), drop task_done()/join() entirely and use sentinel values or task cancellation for shutdown"],"exampleFix":"# before\nwhile True:\n    item = await q.get()\n    q.task_done()\n    await handle(item)   # if handle raises, finally path may call again\n# after\nwhile True:\n    item = await q.get()\n    try:\n        await handle(item)\n    finally:\n        q.task_done()","handlingStrategy":"validation","validationCode":"class TrackedQueue(asyncio.Queue):\n    def __init__(self):\n        super().__init__()\n        self._got = set()\n\n    async def get_tracked(self):\n        item = await super().get()\n        self._got.add(id(item))\n        return item\n\n    def task_done_safe(self, item):\n        if id(item) not in self._got:\n            raise ValueError('task_done without matching get')\n        self._got.discard(id(item))\n        super().task_done()","typeGuard":null,"tryCatchPattern":"try:\n    q.task_done()\nexcept ValueError as e:\n    if 'too many times' in str(e):\n        logger.exception('unbalanced task_done; audit worker bookkeeping')\n    raise","preventionTips":["One task_done per successful get — put it in the finally of the worker loop","Never call task_done in both except and outer finally for the same item","On failure-requeue, put() the item again instead of double task_done","Consider sentinel-based shutdown instead of join/task_done when counts get complex"],"tags":["asyncio","queue","consumer","bookkeeping"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}