{"record":{"id":"884cba357d607838","repo":"sqlalchemy/alembic","slug":"can-not-set-dispatch-function-for-object-target-r","errorCode":null,"errorMessage":"Can not set dispatch function for object {target!r}: key already exists. To replace existing function, use replace=True.","messagePattern":"Can not set dispatch function for object (.+?): key already exists\\. To replace existing function, use replace=True\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"alembic/util/langhelpers.py","lineNumber":312,"sourceCode":"\n    LAST = 10\n    \"\"\"Run the function in the last batch of functions\"\"\"\n\n\nclass Dispatcher:\n    def __init__(self) -> None:\n        self._registry: dict[tuple[Any, ...], Any] = {}\n\n    def dispatch_for(\n        self,\n        target: Any,\n        *,\n        qualifier: str = \"default\",\n        replace: bool = False,\n    ) -> Callable[[_C], _C]:\n        def decorate(fn: _C) -> _C:\n            if (target, qualifier) in self._registry and not replace:\n                raise ValueError(\n                    \"Can not set dispatch function for object \"\n                    f\"{target!r}: key already exists. To replace \"\n                    \"existing function, use replace=True.\"\n                )\n            self._registry[(target, qualifier)] = fn\n            return fn\n\n        return decorate\n\n    def dispatch(self, obj: Any, qualifier: str = \"default\") -> Any:\n        if isinstance(obj, str):\n            targets: Sequence[Any] = [obj]\n        elif isinstance(obj, type):\n            targets = obj.__mro__\n        else:\n            targets = type(obj).__mro__\n\n        if qualifier != \"default\":","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/util/langhelpers.py#L294-L330","documentation":"Raised as ValueError by Dispatcher.dispatch_for when registering a dispatch function for a (target, qualifier) pair that already exists in the registry, unless replace=True is passed (langhelpers.py:311-316). The Dispatcher implements type-based multimethod dispatch — each target type can only have one handler per qualifier. This prevents accidental silent overwrites of registered handlers.","triggerScenarios":"Calling dispatcher.dispatch_for(SomeClass) twice for the same class without replace=True. This happens in Alembic's internal plugin/impl registration, e.g. when registering dialect-specific operations or comparison impls. Third-party plugins that register for an already-registered type also hit this.","commonSituations":"Writing a third-party Alembic implementation (e.g., a custom database dialect impl) and registering a dispatch handler for a type that Alembic already handles. Importing a plugin that double-registers. Test fixtures that register dispatchers without cleaning up between tests.","solutions":["If you intend to replace the existing handler, pass replace=True: dispatcher.dispatch_for(SomeClass, replace=True).","If the registration is unintentional, remove the duplicate dispatch_for call.","Check if the target type is already handled by inspecting dispatcher._registry before registering.","In tests, use dispatcher.branch() to get an independent copy and avoid polluting the global registry."],"exampleFix":"# before\n@dispatcher.dispatch_for(SomeType)\ndef my_handler(obj):\n    ...  # ValueError if SomeType already registered\n\n# after (intentional replacement)\n@dispatcher.dispatch_for(SomeType, replace=True)\ndef my_handler(obj):\n    ...","handlingStrategy":"validation","validationCode":"def is_dispatch_registered(dispatcher, target, qualifier='default'):\n    return (target, qualifier) in dispatcher._registry","typeGuard":null,"tryCatchPattern":"try:\n    @dispatcher.dispatch_for(SomeType)\n    def handler(obj):\n        ...\nexcept ValueError as e:\n    if 'key already exists' in str(e):\n        @dispatcher.dispatch_for(SomeType, replace=True)\n        def handler(obj):\n            ...\n    else:\n        raise","preventionTips":["Check dispatcher._registry before registering if duplicates are possible.","Use replace=True when intentionally overriding a handler.","In tests, use dispatcher.branch() for isolated registrations."],"tags":["alembic","dispatcher","registration","plugins","valueerror"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}