sqlalchemy/alembic · error · ValueError
Can not set dispatch function for object {target!r}: key alr
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 (langhelpers.py:312) when registering a function for a (target, qualifier) pair that already exists in the registry and replace=False (the default). The dispatcher forbids silent overrides to prevent two plugins/impls from unknowingly clobbering each other's handler for the same type.
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 44fb345033)
Solutions
- If you intend to override, pass replace=True: dispatcher.dispatch_for(SomeType, replace=True)(fn).
- Otherwise branch the dispatcher first (dispatcher.branch()) to get an independent writable copy.
- Check 'if (target, qualifier) not in dispatcher._registry' before registering to avoid collisions.
- Ensure you are not double-loading the same plugin/impl module.
Example fix
# before from alembic.util.langhelpers import Dispatcher d = Dispatcher() @d.dispatch_for(int) def h(x): return x @d.dispatch_for(int) # ValueError: key already exists def h2(x): return x*2 # after: explicitly replace d.dispatch_for(int, replace=True) def h2(x): return x*2
Defensive patterns
Strategy: validation
Validate before calling
from alembic.util.langhelpers import Dispatcher
def safe_register(d: Dispatcher, target, fn, qualifier: str = 'default'):
key = (target, qualifier)
if key in d._registry:
raise ValueError(f'{key} already registered; pass replace=True to override')
d.dispatch_for(target, qualifier=qualifier)(fn) Try / catch
from alembic.util.langhelpers import Dispatcher
try:
d.dispatch_for(MyType)(fn)
except ValueError as e:
if 'key already exists' in str(e):
d.dispatch_for(MyType, replace=True)(fn)
else:
raise Prevention
- Check the registry before registering, or always pass replace=True when overriding is intended.
- Use dispatcher.branch() for independent, non-clobbering registries in plugins.
- Avoid loading the same impl/plugin module twice.
When it happens
Trigger: Calling dispatcher.dispatch_for(SomeType)(fn) twice; a plugin or impl registering a dispatch for a type that Alembic (or another plugin) already registered; calling dispatch_for on a branched dispatcher that inherited a parent's entry.
Common situations: Writing a custom Operations implementation or ddl impl that registers a dispatch for a dialect/type Alembic already handles; loading two plugins that both target the same type; calling dispatch_for in a loop without deduping.
Related errors
- no dispatch function for object: %s
- revision %s not in map
- Don't know how to comma-format %r
- Can't drop table in batch mode
- Constraint must have a name
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/884cba357d607838.json.
Report an issue: GitHub.