sqlalchemy/alembic · error · NotImplementedError

The %s method does not apply to a batch table alter operatio

Error message

The %s method does not apply to a batch table alter operation.

What it means

BatchOperations is a narrowed view of Operations scoped to a single table being altered via batch_alter_table. Methods that operate at the schema level (create_table, drop_table, create_index on an unrelated table, etc.) are mapped to _noop, which raises because they are meaningless inside a batch context where the target table is fixed.

Source

Thrown at alembic/operations/base.py:1713

    This basically omits the ``table_name`` and ``schema`` parameters
    from associated methods, as these are a given when running under batch
    mode.

    .. seealso::

        :meth:`.Operations.batch_alter_table`

    Note that as of 0.8, most of the methods on this class are produced
    dynamically using the :meth:`.Operations.register_operation`
    method.

    """

    impl: BatchOperationsImpl

    def _noop(self, operation: Any) -> NoReturn:
        raise NotImplementedError(
            "The %s method does not apply to a batch table alter operation."
            % operation
        )

    if TYPE_CHECKING:
        # START STUB FUNCTIONS: batch_op
        # ### the following stubs are generated by tools/write_pyi.py ###
        # ### do not edit ###

        def add_column(
            self,
            column: Column[Any],
            *,
            insert_before: str | None = None,
            insert_after: str | None = None,
            if_not_exists: bool | None = None,
            inline_references: bool | None = None,
            inline_primary_key: bool | None = None,

View on GitHub (pinned to 44fb345033)

Solutions

  1. Move create_table/drop_table outside the batch_alter_table context and call them on op directly.
  2. If you need to fully redefine the table, use batch_alter_table(recreate='always') and add columns/constraints via batch_op.add_column / create_*_constraint.
  3. Check the BatchOperations stub list to see which methods are valid inside batch mode.

Example fix

// before
with op.batch_alter_table('users') as batch_op:
    batch_op.create_table(sa.table('extra', sa.column('id', sa.Integer)))
// after
op.create_table('extra', sa.Column('id', sa.Integer))
with op.batch_alter_table('users') as batch_op:
    batch_op.add_column(sa.Column('name', sa.String))
Defensive patterns

Strategy: type-guard

Validate before calling

from alembic.operations.base import BatchOperations
if isinstance(op_or_batch, BatchOperations):
    raise RuntimeError("Schema-level ops not allowed inside batch_alter_table; call on op instead")

Type guard

def is_batch_op(obj) -> bool:
    from alembic.operations.base import BatchOperations
    return isinstance(obj, BatchOperations)

Prevention

When it happens

Trigger: Inside a 'with op.batch_alter_table("t") as batch_op:' block, calling one of the schema-level methods that are explicitly registered to _noop (commonly batch_op.create_table(...) or batch_op.drop_table(...)).

Common situations: Autogenerated batch block that accidentally included a create_table; a developer assuming batch_op mirrors the full Operations API; copy-pasting an op.create_table call and renaming op to batch_op.

Related errors


AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04). Data as JSON: /data/errors/5c4f638b771b5e16.json. Report an issue: GitHub.