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
- 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.
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
- 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.
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
- no dispatch function for object
- A plugin named is already registered
- Don't know how to comma-format %r
- Invalid plugin expression
- String or text() construct expected
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)