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

  1. Run the migration online (without --sql) so a real connection is available for run_async.
  2. Guard run_async usage behind a check: only emit async steps when not in SQL mode.
  3. 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

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


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 as

View on GitHub (pinned to 5551b5d35f)