sqlalchemy/alembic · error · ValueError
no dispatch function for object
Error message
no dispatch function for object: %s
What it means
Raised as ValueError by Dispatcher.dispatch when no dispatch function is registered for the given object's type or any of its superclasses in the MRO (langhelpers.py:335-340). The dispatcher walks type(obj).__mro__ looking for a registered handler; if none matches for any qualifier, it raises. This indicates the object type being dispatched hasn't been registered with dispatch_for.
Solutions
- Register a dispatch handler for the object's type: @dispatcher.dispatch_for(MyCustomType).
- If the type has a parent that IS registered, ensure the custom type properly inherits from it so the MRO walk finds the parent handler.
- Check that the plugin/module containing the dispatch_for registration is actually imported before dispatch is called.
- Inspect the object's MRO: print(type(obj).__mro__) to see what types the dispatcher is checking.
Example fix
# before
class MyCustomType(sa.UserDefinedType):
...
# migration uses MyCustomType but autogenerate fails with 'no dispatch function'
# after
from alembic.autogenerate.api import AutogenContext
from alembic.util.langhelpers import Dispatcher
# register a comparator for the custom type
@comparator.dispatch_for(MyCustomType)
def _compare_my_type(autogen_context, ops, sqla_type):
... Defensive patterns
Strategy: validation
Validate before calling
def has_dispatch_for_type(dispatcher, obj, qualifier='default'):
targets = type(obj).__mro__ if not isinstance(obj, str) else [obj]
if isinstance(obj, type):
targets = obj.__mro__
qualifiers = [qualifier, 'default'] if qualifier != 'default' else ['default']
for t in targets:
for q in qualifiers:
if (t, q) in dispatcher._registry:
return True
return False Try / catch
try:
result = dispatcher.dispatch(obj)
except ValueError as e:
if 'no dispatch function' in str(e):
print(f"No handler for {type(obj)}. Register one with dispatch_for.")
raise Prevention
- Register dispatch handlers for all custom types before using them in migrations.
- Ensure custom types inherit from a base class that already has a registered handler.
- Verify that plugin modules containing dispatch_for registrations are imported at startup.
When it happens
Trigger: Calling dispatcher.dispatch(obj) or dispatcher.dispatch(obj, qualifier='custom') where obj's type (and all its base classes) have no registered handler. In Alembic this occurs in autogenerate comparison and operation rendering — e.g., rendering a custom SQLAlchemy type or operation class that hasn't been registered with the relevant dispatcher.
Common situations: Using a custom SQLAlchemy column type in a migration without registering a comparator/rendering impl. Third-party operation classes not registered with the operations dispatcher. Passing an unexpected object type to an internal dispatch API. Version mismatch where a plugin's registration didn't load.
Related errors
- Can not set 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/ef145b44511390ae.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/util/langhelpers.py:340
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":
qualifiers = [qualifier, "default"]
else:
qualifiers = ["default"]
for spcls in targets:
for qualifier in qualifiers:
if (spcls, qualifier) in self._registry:
return self._registry[(spcls, qualifier)]
else:
raise ValueError("no dispatch function for object: %s" % obj)
def branch(self) -> Dispatcher:
"""Return a copy of this dispatcher that is independently
writable."""
d = Dispatcher()
d._registry.update(self._registry)
return d
class PriorityDispatcher:
"""registers lists of functions at multiple levels of priority and provides
a target to invoke them in priority order.
.. versionadded:: 1.18.0 - PriorityDispatcher replaces the job
of Dispatcher(uselist=True)
"""View on GitHub (pinned to 5551b5d35f)