{"record":{"id":"42a6b943b5b896a5","repo":"sqlalchemy/alembic","slug":"can-t-create-table-in-batch-mode","errorCode":null,"errorMessage":"Can't create table in batch mode","messagePattern":"Can't create table in batch mode","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"alembic/operations/batch.py","lineNumber":198,"sourceCode":"        self.batch.append((\"drop_constraint\", (const,), {}))\n\n    def rename_table(self, *arg, **kw):\n        self.batch.append((\"rename_table\", arg, kw))\n\n    def create_index(self, idx: Index, **kw: Any) -> None:\n        self.batch.append((\"create_index\", (idx,), kw))\n\n    def drop_index(self, idx: Index, **kw: Any) -> None:\n        self.batch.append((\"drop_index\", (idx,), kw))\n\n    def create_table_comment(self, table):\n        self.batch.append((\"create_table_comment\", (table,), {}))\n\n    def drop_table_comment(self, table):\n        self.batch.append((\"drop_table_comment\", (table,), {}))\n\n    def create_table(self, table):\n        raise NotImplementedError(\"Can't create table in batch mode\")\n\n    def drop_table(self, table):\n        raise NotImplementedError(\"Can't drop table in batch mode\")\n\n    def create_column_comment(self, column):\n        self.batch.append((\"create_column_comment\", (column,), {}))\n\n\nclass ApplyBatchImpl:\n    def __init__(\n        self,\n        impl: DefaultImpl,\n        table: Table,\n        table_args: tuple,\n        table_kwargs: dict[str, Any],\n        reflected: bool,\n        partial_reordering: tuple = (),\n    ) -> None:","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/operations/batch.py#L180-L216","documentation":"BatchOperationsImpl.create_table raises NotImplementedError because creating an entirely new table is not meaningful inside a batch_alter_table block, which operates on an existing table by rebuilding it. The batch context only supports modifications to the target table, not creating additional tables.","triggerScenarios":"Calling batch_op.create_table(...) (or having Alembic route create_table through the batch impl) inside a with op.batch_alter_table(...) block.","commonSituations":"Calling the wrong op alias inside a batch block; a generic code path that dispatches create_table uniformly across batch and non-batch contexts.","solutions":["Move op.create_table(...) outside the batch_alter_table block, calling it on the top-level Operations object.","If the new table must be created alongside the batched alter, finish the batch block first, then create the table.","Ensure code that conditionally creates tables does not run while a batch context is active."],"exampleFix":"# before\nwith op.batch_alter_table('t') as batch_op:\n    batch_op.alter_column('c', type_=sa.Integer())\n    batch_op.create_table('other', ...)  # error\n\n# after\nwith op.batch_alter_table('t') as batch_op:\n    batch_op.alter_column('c', type_=sa.Integer())\nop.create_table('other', ...)  # outside batch","handlingStrategy":"validation","validationCode":"def safe_batch_block(op, table, batch_fn, *create_after):\n    with op.batch_alter_table(table) as batch_op:\n        batch_fn(batch_op)\n    for create in create_after:\n        op.create_table(create)  # always outside batch","typeGuard":"def is_outside_batch(op) -> bool:\n    # batch ops proxy is not the top-level Operations instance\n    return type(op).__name__ == 'Operations'","tryCatchPattern":null,"preventionTips":["Never call create_table inside a batch_alter_table block.","Finish the batch context before creating additional tables.","Use op (not batch_op) for any whole-table or whole-DB operation."],"tags":["batch-mode","create-table","not-supported","ddl"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}