sqlalchemy/alembic · error · ValueError

A plugin named {name} is already registered

Error message

A plugin named {name} is already registered

What it means

Raised by Plugin.__init__() when a Plugin with the given name already exists in the global _all_plugins registry. Plugin names must be unique process-wide because the autogenerate comparator dispatch and branch-label mapping key off them. The check runs at construction, which happens automatically for any module published via the alembic.plugins entry point and on manual Plugin() creation.

Source

Thrown at alembic/runtime/plugins.py:39

log = logging.getLogger(__name__)


class Plugin:
    """Describe a series of functions that are pulled in as a plugin.

    This is initially to provide for portable lists of autogenerate
    comparison functions, however the setup for a plugin can run any
    other kinds of global registration as well.

    .. versionadded:: 1.18.0

    """

    def __init__(self, name: str):
        self.name = name
        log.info("setup plugin %s", name)
        if name in _all_plugins:
            raise ValueError(f"A plugin named {name} is already registered")
        _all_plugins[name] = self
        self.autogenerate_comparators = PriorityDispatcher()

    def remove(self) -> None:
        """remove this plugin"""

        del _all_plugins[self.name]

    def add_autogenerate_comparator(
        self,
        fn: Callable[..., PriorityDispatchResult],
        compare_target: str,
        compare_element: str | None = None,
        *,
        qualifier: str = "default",
        priority: DispatchPriority = DispatchPriority.MEDIUM,
    ) -> None:
        """Register an autogenerate comparison function.

View on GitHub (pinned to 44fb345033)

Solutions

  1. Give each plugin a unique entry point / Plugin() name (namespace with package prefix).
  2. In test teardown call plugin.remove() to deregister before re-registering.
  3. Audit installed packages' alembic.plugins entry points and rename the duplicate.

Example fix

// before
# two packages both declare entry point name 'myplugin'
Plugin('myplugin')  # second call raises ValueError

// after
# give unique names
Plugin('pkg_a.myplugin')
Plugin('pkg_b.myplugin')
Defensive patterns

Strategy: validation

Validate before calling

from alembic.runtime.plugins import _all_plugins

def plugin_name_available(name) -> bool:
    return name not in _all_plugins

Try / catch

from alembic.runtime.plugins import Plugin, _all_plugins
try:
    Plugin(name)
except ValueError as e:
    if 'already registered' in str(e):
        existing = _all_plugins[name]
        existing.remove()
        Plugin(name)
    else:
        raise

Prevention

When it happens

Trigger: Two installed packages declaring the same alembic.plugins entry point name; manually calling Plugin('name') twice; an entry point and a manual registration both using 'name'; re-importing a plugin module after a failed remove() in a long-lived process or test session.

Common situations: Two third-party libraries shipping alembic plugins under the same entry point name; a plugin left in _all_plugins between tests because remove() wasn't called; a local plugin whose name collides with a published one.

Related errors


AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04). Data as JSON: /data/errors/daa6541c1d1eca29.json. Report an issue: GitHub.