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__ validates the recreate argument against the fixed set {'auto','always','never'} because these three encode distinct table-rebuild strategies: auto lets the dialect decide, always forces a copy-and-move rebuild, never forbids it. Any other value is a configuration typo that would silently fall through to never.

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 44fb345033)

Solutions

  1. Use recreate='auto' (default) to let the dialect decide based on the operation mix.
  2. Use recreate='always' to force a full table rebuild (e.g. for SQLite constraint changes).
  3. Use recreate='never' to forbid rebuild (will error if the operation requires it).

Example fix

// before
with op.batch_alter_table('t', recreate=True) as batch_op:  # bool -> ValueError
    ...
// after
with op.batch_alter_table('t', recreate='always') as batch_op:
    ...
Defensive patterns

Strategy: validation

Validate before calling

if recreate not in ("auto", "always", "never"):
    raise ValueError("recreate must be one of 'auto','always','never'")

Type guard

def is_valid_recreate(v) -> bool:
    return v in ("auto", "always", "never")

Prevention

When it happens

Trigger: Passing recreate= to batch_alter_table with a value like 'yes', 'true', 'false', True (bool), None, or a misspelled string.

Common situations: A boolean passed instead of a string (recreate=True); a string copied from a tutorial that used a non-standard token; a config-driven value that wasn't validated upstream.

Related errors


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