{"record":{"id":"400c5fcc53198109","repo":"sqlalchemy/alembic","slug":"cannot-call-run-async-in-sql-mode","errorCode":null,"errorMessage":"Cannot call run_async in SQL mode","messagePattern":"Cannot call run_async in SQL mode","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"alembic/operations/base.py","lineNumber":577,"sourceCode":"\n        The async connection passed to the callable shares the same\n        transaction as the connection running in the migration context.\n\n        Any additional arg or kw_arg passed to this function are passed\n        to the provided async function.\n\n        .. versionadded: 1.11\n\n        .. note::\n\n            This method can be called only when alembic is called using\n            an async dialect.\n        \"\"\"\n        if not sqla_compat.sqla_14_18:\n            raise NotImplementedError(\"SQLAlchemy 1.4.18+ required\")\n        sync_conn = self.get_bind()\n        if sync_conn is None:\n            raise NotImplementedError(\"Cannot call run_async in SQL mode\")\n        if not sync_conn.dialect.is_async:\n            raise ValueError(\"Cannot call run_async with a sync engine\")\n        from sqlalchemy.ext.asyncio import AsyncConnection\n        from sqlalchemy.util import await_only\n\n        async_conn = AsyncConnection._retrieve_proxy_for_target(sync_conn)\n        return await_only(async_function(async_conn, *args, **kw_args))\n\n\nclass Operations(AbstractOperations):\n    \"\"\"Define high level migration operations.\n\n    Each operation corresponds to some schema migration operation,\n    executed against a particular :class:`.MigrationContext`\n    which in turn represents connectivity to a database,\n    or a file output stream.\n\n    While :class:`.Operations` is normally configured as","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/operations/base.py#L559-L595","documentation":"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.","triggerScenarios":"Calling op.run_async(fn) inside a migration run with `alembic upgrade head --sql` or a MigrationContext configured with as_sql=True (no engine).","commonSituations":"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.","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."],"exampleFix":"# before: invoked regardless of mode\nasync def seed(async_conn): ...\nop.run_async(seed)\n\n# after: skip in offline mode\nfrom alembic import context\nif not context.is_offline_mode():\n    op.run_async(seed)\nelse:\n    op.execute('INSERT INTO ... ')","handlingStrategy":"validation","validationCode":"from alembic import context\n\ndef run_async_if_online(op, fn, *args, **kw):\n    if context.is_offline_mode():\n        raise RuntimeError('run_async is not available in SQL/offline mode')\n    return op.run_async(fn, *args, **kw)","typeGuard":"def is_online(op) -> bool:\n    return op.get_bind() is not None","tryCatchPattern":null,"preventionTips":["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."],"tags":["run-async","offline-mode","as-sql","async"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}