{"id":"32a1570c22645a30","repo":"celery/celery","slug":"chain-is-not-a-real-task","errorCode":null,"errorMessage":"chain is not a real task","messagePattern":"chain is not a real task","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"celery/app/builtins.py","lineNumber":161,"sourceCode":"        # any partial args are added to all tasks in the group\n        taskit = (maybe_signature(task, app=app).clone(partial_args)\n                  for i, task in enumerate(tasks))\n        with app.producer_or_acquire() as producer:\n            [stask.apply_async(group_id=group_id, producer=producer,\n                               add_to_parent=False) for stask in taskit]\n        parent = app.current_worker_task\n        if add_to_parent and parent:\n            parent.add_trail(result)\n        return result\n    return group\n\n\n@connect_on_app_finalize\ndef add_chain_task(app):\n    \"\"\"No longer used, but here for backwards compatibility.\"\"\"\n    @app.task(name='celery.chain', shared=False, lazy=False)\n    def chain(*args, **kwargs):\n        raise NotImplementedError('chain is not a real task')\n    return chain\n\n\n@connect_on_app_finalize\ndef add_chord_task(app):\n    \"\"\"No longer used, but here for backwards compatibility.\"\"\"\n    from celery import chord as _chord\n    from celery import group\n    from celery.canvas import maybe_signature\n\n    @app.task(name='celery.chord', bind=True, ignore_result=False,\n              shared=False, lazy=False)\n    def chord(self, header, body, partial_args=(), interval=None,\n              countdown=1, max_retries=None, eager=False, **kwargs):\n        app = self.app\n        # - convert back to group if serialized\n        tasks = header.tasks if isinstance(header, group) else header\n        header = group([","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/celery/celery/blob/571efe81202341310b6257304980ba7898ab0f60/celery/app/builtins.py#L143-L179","documentation":"Raised by the backwards-compatibility 'celery.chain' task registered in celery/app/builtins.py. Historically a task named 'celery.chain' existed in the registry; the modern chain is a canvas primitive (celery.chain), not an executable task. The stub task raises NotImplementedError if someone sends a task message addressed to the name 'celery.chain' (e.g. via send_task('celery.chain')).","triggerScenarios":"Calling app.send_task('celery.chain', ...) or app.send_task('celery.chord') against a legacy task name; serializing/deserializing a chain and mistakenly dispatching it as a task; old client code or a stale queue message referencing the 'celery.chain' task name.","commonSituations":"Upgrading from a very old Celery version where these were real tasks; leftover messages in the broker queue referencing 'celery.chain'; third-party tooling that dispatches by task name without using the canvas API.","solutions":["Use the canvas API: from celery import chain; chain(task1.s(), task2.s()).apply_async().","Clear stale 'celery.chain' messages from the broker queue if upgrading.","Audit third-party code that calls send_task('celery.chain') and switch it to the canvas primitive.","Do not reference the built-in 'celery.chain' name in routing or task_routes."],"exampleFix":"# before\napp.send_task('celery.chain', args=[...])\n\n# after\nfrom celery import chain\nchain(step1.s(2), step2.s()).apply_async()","handlingStrategy":"validation","validationCode":"from celery import chain\n\n# never call send_task on the legacy name\nBANNED_NAMES = {'celery.chain', 'celery.chord'}\ndef safe_send(app, name, *a, **k):\n    if name in BANNED_NAMES:\n        raise ValueError(f'{name} is a canvas primitive, not a dispatchable task; use celery.{name.split(\".\")[1]}')\n    return app.send_task(name, args=a, kwargs=k)","typeGuard":"def is_legacy_builtin_task_name(name: str) -> bool:\n    return name in ('celery.chain',)","tryCatchPattern":null,"preventionTips":["Use the canvas API (celery.chain, celery.group, celery.chord) instead of send_task for composition.","Purge stale broker queues referencing 'celery.chain' after upgrades.","Lint code for send_task calls using built-in canvas names."],"tags":["celery","canvas","chain","legacy","not-implemented"],"analyzedSha":"571efe81202341310b6257304980ba7898ab0f60","analyzedAt":"2026-08-04T20:17:20.567Z","schemaVersion":2}