{"record":{"id":"ef145b44511390ae","repo":"sqlalchemy/alembic","slug":"no-dispatch-function-for-object-s","errorCode":null,"errorMessage":"no dispatch function for object: %s","messagePattern":"no dispatch function for object: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"alembic/util/langhelpers.py","lineNumber":340,"sourceCode":"    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\":\n            qualifiers = [qualifier, \"default\"]\n        else:\n            qualifiers = [\"default\"]\n\n        for spcls in targets:\n            for qualifier in qualifiers:\n                if (spcls, qualifier) in self._registry:\n                    return self._registry[(spcls, qualifier)]\n        else:\n            raise ValueError(\"no dispatch function for object: %s\" % obj)\n\n    def branch(self) -> Dispatcher:\n        \"\"\"Return a copy of this dispatcher that is independently\n        writable.\"\"\"\n\n        d = Dispatcher()\n        d._registry.update(self._registry)\n        return d\n\n\nclass PriorityDispatcher:\n    \"\"\"registers lists of functions at multiple levels of priority and provides\n    a target to invoke them in priority order.\n\n    .. versionadded:: 1.18.0 - PriorityDispatcher replaces the job\n       of Dispatcher(uselist=True)\n\n    \"\"\"","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/util/langhelpers.py#L322-L358","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nclass MyCustomType(sa.UserDefinedType):\n    ...\n\n# migration uses MyCustomType but autogenerate fails with 'no dispatch function'\n\n# after\nfrom alembic.autogenerate.api import AutogenContext\nfrom alembic.util.langhelpers import Dispatcher\n\n# register a comparator for the custom type\n@comparator.dispatch_for(MyCustomType)\ndef _compare_my_type(autogen_context, ops, sqla_type):\n    ...","handlingStrategy":"validation","validationCode":"def has_dispatch_for_type(dispatcher, obj, qualifier='default'):\n    targets = type(obj).__mro__ if not isinstance(obj, str) else [obj]\n    if isinstance(obj, type):\n        targets = obj.__mro__\n    qualifiers = [qualifier, 'default'] if qualifier != 'default' else ['default']\n    for t in targets:\n        for q in qualifiers:\n            if (t, q) in dispatcher._registry:\n                return True\n    return False","typeGuard":null,"tryCatchPattern":"try:\n    result = dispatcher.dispatch(obj)\nexcept ValueError as e:\n    if 'no dispatch function' in str(e):\n        print(f\"No handler for {type(obj)}. Register one with dispatch_for.\")\n    raise","preventionTips":["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."],"tags":["alembic","dispatcher","plugins","autogenerate","valueerror"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}