sqlalchemy/alembic · error · ValueError

Cannot call run_async with a sync engine

Error message

Cannot call run_async with a sync engine

What it means

Operations.run_async raises ValueError when the underlying connection's dialect is not async (sync_conn.dialect.is_async is False). run_async wraps an AsyncConnection derived from a synchronous proxy that only exists for async-configured engines; a plain sync engine has no async proxy to retrieve.

Solutions

  1. Configure env.py to create an async engine (create_async_engine) and run migrations via connection.run_sync().
  2. Use an async DBAPI driver in sqlalchemy.asyncio.create_async_engine (e.g. asyncpg, aiomysql).
  3. If you must use a sync engine, replace run_async with synchronous logic on op.get_bind().

Example fix

# before: sync engine in env.py
engine = create_engine('postgresql+psycopg2://...')

# after: async engine
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine('postgresql+asyncpg://...')
# then run migrations with connection.run_sync(do_run_migrations)
Defensive patterns

Strategy: validation

Validate before calling

def run_async_safe(op, fn, *args, **kw):
    bind = op.get_bind()
    if bind is None or not bind.dialect.is_async:
        raise ValueError('run_async requires an async engine')
    return op.run_async(fn, *args, **kw)

Type guard

def engine_is_async(bind) -> bool:
    return bool(getattr(getattr(bind, 'dialect', None), 'is_async', False))

Prevention

When it happens

Trigger: Calling op.run_async(fn) in a migration whose engine was created with a sync driver (e.g. psycopg2, mysql+pymysql) rather than an async driver (e.g. postgresql+asyncpg, mysql+aiomysql).

Common situations: Switching a project to asyncpg but env.py still creates a sync engine; mixing sync alembic runs with run_async calls; tests using a sync SQLite engine while migrations assume async.

Related errors


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

Appendix: source

Thrown at alembic/operations/base.py:579

        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,
    or a file output stream.

    While :class:`.Operations` is normally configured as
    part of the :meth:`.EnvironmentContext.run_migrations`
    method called from an ``env.py`` script, a standalone

View on GitHub (pinned to 5551b5d35f)