sqlalchemy/alembic · error · ValueError

recreate may be one of 'auto', 'always', or 'never'.

Error message

recreate may be one of 'auto', 'always', or 'never'.

What it means

BatchOperationsImpl.__init__ raises ValueError when the recreate parameter of batch_alter_table() is not one of 'auto', 'always', or 'never'. The value controls whether the table is rebuilt via copy-and-move: 'auto' decides based on the operation, 'always' forces a rebuild, 'never' forbids it (erroring if the operation requires one).

Solutions

  1. Use recreate='auto' (default) to let Alembic decide based on the dialect and operations.
  2. Use recreate='always' to force a table rebuild (needed for SQLite and some constrained operations).
  3. Use recreate='never' to forbid rebuilds (only valid on dialects supporting the needed ALTERs natively).

Example fix

# before
with op.batch_alter_table('t', recreate=True) as batch_op:
    ...

# after
with op.batch_alter_table('t', recreate='always') as batch_op:
    ...
Defensive patterns

Strategy: validation

Validate before calling

VALID_RECREATE = {'auto', 'always', 'never'}

def safe_batch(op, table, recreate='auto', **kw):
    if recreate not in VALID_RECREATE:
        raise ValueError(f"recreate must be one of {VALID_RECREATE}")
    return op.batch_alter_table(table, recreate=recreate, **kw)

Type guard

def is_valid_recreate(value: str) -> bool:
    return value in {'auto', 'always', 'never'}

Prevention

When it happens

Trigger: Calling op.batch_alter_table('t', recreate='sometimes') or passing a typo/boolean/None as recreate.

Common situations: Typos in the recreate value; passing recreate=True instead of recreate='always'; outdated tutorial using an invalid string.

Related errors


AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11). Data as JSON: /api/errors/07d0fa549eee11bd. Report an issue: GitHub.

Appendix: source

Thrown at alembic/operations/batch.py:68

    def __init__(
        self,
        operations,
        table_name,
        schema,
        recreate,
        copy_from,
        table_args,
        table_kwargs,
        reflect_args,
        reflect_kwargs,
        naming_convention,
        partial_reordering,
    ):
        self.operations = operations
        self.table_name = table_name
        self.schema = schema
        if recreate not in ("auto", "always", "never"):
            raise ValueError(
                "recreate may be one of 'auto', 'always', or 'never'."
            )
        self.recreate = recreate
        self.copy_from = copy_from
        self.table_args = table_args
        self.table_kwargs = dict(table_kwargs)
        self.reflect_args = reflect_args
        self.reflect_kwargs = dict(reflect_kwargs)
        self.reflect_kwargs.setdefault(
            "listeners", list(self.reflect_kwargs.get("listeners", ()))
        )
        self.reflect_kwargs["listeners"].append(
            ("column_reflect", operations.impl.autogen_column_reflect)
        )
        self.naming_convention = naming_convention
        self.partial_reordering = partial_reordering
        self.batch = []

View on GitHub (pinned to 5551b5d35f)