sqlalchemy/alembic · error · NotImplementedError
Cannot call run_async in SQL mode
Error message
Cannot call run_async in SQL mode
What it means
Operations.run_async raises NotImplementedError when called in offline/SQL mode (get_bind() returns None). run_async needs a live synchronous connection to derive the underlying AsyncConnection proxy; with no connection there is nothing to await on, so it is rejected.
Solutions
- Run the migration online (without --sql) so a real connection is available for run_async.
- Guard run_async usage behind a check: only emit async steps when not in SQL mode.
- Move the async-dependent logic into application code or a separate online-only migration step.
Example fix
# before: invoked regardless of mode
async def seed(async_conn): ...
op.run_async(seed)
# after: skip in offline mode
from alembic import context
if not context.is_offline_mode():
op.run_async(seed)
else:
op.execute('INSERT INTO ... ') Defensive patterns
Strategy: validation
Validate before calling
from alembic import context
def run_async_if_online(op, fn, *args, **kw):
if context.is_offline_mode():
raise RuntimeError('run_async is not available in SQL/offline mode')
return op.run_async(fn, *args, **kw) Type guard
def is_online(op) -> bool:
return op.get_bind() is not None Prevention
- Gate run_async calls behind context.is_offline_mode() checks.
- Keep async data operations in online-only migration steps.
- Test migrations in both --sql and online modes to surface offline-only code.
When it happens
Trigger: Calling op.run_async(fn) inside a migration run with `alembic upgrade head --sql` or a MigrationContext configured with as_sql=True (no engine).
Common situations: Generating SQL scripts for deployments that still include async data operations; CI that runs --sql for review and online for apply, hitting run_async in the former.
Related errors
- Cannot call run_async with a sync engine
- SQL parameters not allowed with as_sql
- SQLAlchemy 1.4.18+ required
- can't return inspector as this AutogenContext has no…
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/400c5fcc53198109.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/operations/base.py:577
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,
or a file output stream.
While :class:`.Operations` is normally configured asView on GitHub (pinned to 5551b5d35f)