sqlalchemy/alembic · error · ValueError

Invalid plugin expression {name!r}

Error message

Invalid plugin expression {name!r}

What it means

Raised by _make_re() when parsing a plugin include/exclude expression whose tokens are neither '*' nor valid Python identifiers. Plugin expressions are dot-separated tokens used in include_plugins matching; an invalid character or malformed token makes the expression unusable, so ValueError is raised with the offending expression.

Source

Thrown at alembic/runtime/plugins.py:166

        This exact process is invoked automatically at import time for any
        plugin module that is published via the ``alembic.plugins`` entrypoint.

        """
        module.setup(Plugin(name))


def _make_re(name: str) -> Pattern[str]:
    tokens = name.split(".")

    reg = r""
    for token in tokens:
        if token == "*":
            reg += r"\..+?"
        elif token.isidentifier():
            reg += r"\." + token
        else:
            raise ValueError(f"Invalid plugin expression {name!r}")

    # omit leading r'\.'
    return re.compile(f"^{reg[2:]}$")


def _setup() -> None:
    # setup third party plugins
    for entrypoint in metadata.entry_points(group="alembic.plugins"):
        for mod in entrypoint.load():
            Plugin.setup_plugin_from_module(mod, entrypoint.name)


_setup()

View on GitHub (pinned to 44fb345033)

Solutions

  1. Use only valid Python identifiers and '*' wildcards, dot-separated (e.g. 'mypkg.*', '~mypkg.experimental').
  2. Prefix-include/exclude with '~' for exclusion and '*' as a wildcard segment.
  3. Verify the 'plugins' option in env.py / configure() against the documented grammar.

Example fix

// before
context.configure(..., plugins=['my-plugin'])  # raises ValueError

// after
context.configure(..., plugins=['my_plugin'])  # valid identifier
Defensive patterns

Strategy: validation

Validate before calling

import re

def is_valid_plugin_expr(expr: str) -> bool:
    if expr.startswith('~'):
        expr = expr[1:]
    for token in expr.split('.'):
        if token == '*':
            continue
        if not token.isidentifier():
            return False
    return True

Type guard

def is_plugin_expr(expr: str) -> bool:
    body = expr[1:] if expr.startswith('~') else expr
    return all(t == '*' or t.isidentifier() for t in body.split('.'))

Try / catch

try:
    Plugin.populate_autogenerate_priority_dispatch(comparators, include_plugins=exprs)
except ValueError as e:
    if 'Invalid plugin expression' in str(e):
        exprs = [e2 for e2 in exprs if is_valid_plugin_expr(e2)]
        Plugin.populate_autogenerate_priority_dispatch(comparators, include_plugins=exprs)
    else:
        raise

Prevention

When it happens

Trigger: Passing an invalid plugin expression to Plugin.populate_autogenerate_priority_dispatch(..., include_plugins=[...]) such as 'my-plugin' (hyphen), 'my plugin' (space), 'my!plugin', or a token starting with a digit; misconfiguring the 'plugins' option in env.py.

Common situations: Typing a plugin name with hyphens or spaces in the alembic 'plugins' config option; copy-pasting a package name (with hyphens) as a plugin expression instead of the dotted module path.

Related errors


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