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
- Configure env.py to create an async engine (create_async_engine) and run migrations via connection.run_sync().
- Use an async DBAPI driver in sqlalchemy.asyncio.create_async_engine (e.g. asyncpg, aiomysql).
- 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
- Create the engine with sqlalchemy.ext.asyncio.create_async_engine for async migrations.
- Use an async DBAPI driver (asyncpg, aiomysql, aiosqlite).
- Verify bind.dialect.is_async before invoking run_async in shared migration code.
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
- Cannot call run_async in SQL mode
- SQLAlchemy 1.4.18+ required
- Individual alter column constructs not supported by MySQL
- No generic 'DROP CONSTRAINT' in MySQL - please specify…
- No support for ALTER of constraints in SQLite dialect…
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 standaloneView on GitHub (pinned to 5551b5d35f)