sqlalchemy/alembic · error · NotImplementedError

SQLAlchemy 1.4.18+ required

Error message

SQLAlchemy 1.4.18+ required

What it means

Operations.run_async raises NotImplementedError when SQLAlchemy is older than 1.4.18. run_async relies on AsyncConnection._retrieve_proxy_for_target and await_only, both introduced in SQLAlchemy 1.4.18, so earlier versions cannot support calling async functions from sync migration code.

Solutions

  1. Upgrade SQLAlchemy: pip install 'SQLAlchemy>=1.4.18' (preferably the latest 2.x).
  2. Pin SQLAlchemy>=1.4.18 in requirements.txt / pyproject.toml to prevent regressions.
  3. If upgrade is impossible, avoid run_async and perform the work with a synchronous connection instead.

Example fix

# before: pinned old SQLA
# requirements.txt
SQLAlchemy==1.3.24

# after
SQLAlchemy>=1.4.18
Defensive patterns

Strategy: validation

Validate before calling

import sqlalchemy
from alembic.util import sqla_compat

def can_run_async() -> bool:
    return bool(sqla_compat.sqla_14_18)

if not can_run_async():
    raise RuntimeError(f'SQLAlchemy>=1.4.18 required, got {sqlalchemy.__version__}')

Type guard

def sqlalchemy_supports_run_async() -> bool:
    import sqlalchemy
    return tuple(int(x) for x in sqlalchemy.__version__.split('.')[:3]) >= (1, 4, 18)

Prevention

When it happens

Trigger: Calling op.run_async(some_async_fn) in a migration while the installed SQLAlchemy is < 1.4.18 (sqla_compat.sqla_14_18 is False).

Common situations: Pinned older SQLAlchemy in a legacy project; virtualenv with a downgraded SQLAlchemy; fresh checkout in an environment with an old requirements lock.

Related errors


AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11). Data as JSON: /api/errors/e0413b1231c6c902. Report an issue: GitHub.

Appendix: source

Thrown at alembic/operations/base.py:574

        This method allows calling async functions from within the
        synchronous ``upgrade()`` or ``downgrade()`` alembic migration
        method.

        The async connection passed to the callable shares the same
        transaction as the connection running in the migration context.

        Any additional arg or kw_arg passed to this function are passed
        to the provided async function.

        .. versionadded: 1.11

        .. note::

            This method can be called only when alembic is called using
            an async dialect.
        """
        if not sqla_compat.sqla_14_18:
            raise NotImplementedError("SQLAlchemy 1.4.18+ required")
        sync_conn = self.get_bind()
        if sync_conn is None:
            raise NotImplementedError("Cannot call run_async in SQL mode")
        if not sync_conn.dialect.is_async:
            raise ValueError("Cannot call run_async with a sync engine")
        from sqlalchemy.ext.asyncio import AsyncConnection
        from sqlalchemy.util import await_only

        async_conn = AsyncConnection._retrieve_proxy_for_target(sync_conn)
        return await_only(async_function(async_conn, *args, **kw_args))


class Operations(AbstractOperations):
    """Define high level migration operations.

    Each operation corresponds to some schema migration operation,
    executed against a particular :class:`.MigrationContext`
    which in turn represents connectivity to a database,

View on GitHub (pinned to 5551b5d35f)