sqlalchemy/alembic · error · ValueError

Can not set dispatch function for object

Error message

Can not set dispatch function for object {target!r}: key already exists. To replace existing function, use replace=True.

What it means

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.

Solutions

  1. If you intend to replace the existing handler, pass replace=True: dispatcher.dispatch_for(SomeClass, replace=True).
  2. If the registration is unintentional, remove the duplicate dispatch_for call.
  3. Check if the target type is already handled by inspecting dispatcher._registry before registering.
  4. In tests, use dispatcher.branch() to get an independent copy and avoid polluting the global registry.

Example fix

# before
@dispatcher.dispatch_for(SomeType)
def my_handler(obj):
    ...  # ValueError if SomeType already registered

# after (intentional replacement)
@dispatcher.dispatch_for(SomeType, replace=True)
def my_handler(obj):
    ...
Defensive patterns

Strategy: validation

Validate before calling

def is_dispatch_registered(dispatcher, target, qualifier='default'):
    return (target, qualifier) in dispatcher._registry

Try / catch

try:
    @dispatcher.dispatch_for(SomeType)
    def handler(obj):
        ...
except ValueError as e:
    if 'key already exists' in str(e):
        @dispatcher.dispatch_for(SomeType, replace=True)
        def handler(obj):
            ...
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11). Data as JSON: /api/errors/884cba357d607838. Report an issue: GitHub.

Appendix: source

Thrown at alembic/util/langhelpers.py:312

    LAST = 10
    """Run the function in the last batch of functions"""


class Dispatcher:
    def __init__(self) -> None:
        self._registry: dict[tuple[Any, ...], Any] = {}

    def dispatch_for(
        self,
        target: Any,
        *,
        qualifier: str = "default",
        replace: bool = False,
    ) -> Callable[[_C], _C]:
        def decorate(fn: _C) -> _C:
            if (target, qualifier) in self._registry and not replace:
                raise ValueError(
                    "Can not set dispatch function for object "
                    f"{target!r}: key already exists. To replace "
                    "existing function, use replace=True."
                )
            self._registry[(target, qualifier)] = fn
            return fn

        return decorate

    def dispatch(self, obj: Any, qualifier: str = "default") -> Any:
        if isinstance(obj, str):
            targets: Sequence[Any] = [obj]
        elif isinstance(obj, type):
            targets = obj.__mro__
        else:
            targets = type(obj).__mro__

        if qualifier != "default":

View on GitHub (pinned to 5551b5d35f)