sqlalchemy/alembic · error · NotImplementedError
Individual alter column constructs not supported by MySQL
Error message
Individual alter column constructs not supported by MySQL
What it means
MySQL/MariaDB do not support per-attribute ALTER COLUMN clauses like 'ALTER COLUMN ... NULL', 'ALTER COLUMN ... DEFAULT', or standalone RENAME via SQLAlchemy's ColumnNullable/ColumnName/ColumnDefault/ColumnType constructs. Alembic's MySQL dialect compiles those individual alter constructs to an explicit NotImplementedError so that migrations do not silently emit no-op SQL.
Source
Thrown at alembic/ddl/mysql.py:439
if type_ is None:
raise util.CommandError(
"All MySQL CHANGE/MODIFY COLUMN operations "
"require the existing type."
)
self.type_ = sqltypes.to_instance(type_)
class MySQLModifyColumn(MySQLChangeColumn):
pass
@compiles(ColumnNullable, "mysql", "mariadb")
@compiles(ColumnName, "mysql", "mariadb")
@compiles(ColumnDefault, "mysql", "mariadb")
@compiles(ColumnType, "mysql", "mariadb")
def _mysql_doesnt_support_individual(element, compiler, **kw):
raise NotImplementedError(
"Individual alter column constructs not supported by MySQL"
)
@compiles(MySQLAlterDefault, "mysql", "mariadb")
def _mysql_alter_default(
element: MySQLAlterDefault, compiler: MySQLDDLCompiler, **kw
) -> str:
return "%s ALTER COLUMN %s %s" % (
alter_table(compiler, element.table_name, element.schema),
format_column_name(compiler, element.column_name),
(
"SET DEFAULT %s" % format_server_default(compiler, element.default)
if element.default is not None
else "DROP DEFAULT"
),
)
View on GitHub (pinned to 44fb345033)
Solutions
- Use op.batch_alter_table('t') as batch_op: batch_op.alter_column('c', nullable=True, ...) which emits MySQL's single ALTER TABLE ... MODIFY COLUMN statement.
- For a MySQL-native approach, issue op.execute('ALTER TABLE t MODIFY COLUMN c INT NULL') directly.
- When autogenerating against MySQL, prefer batch_alter_table so all column changes collapse into one MODIFY.
Example fix
// before
op.alter_column('users', 'email', nullable=True) # MySQL -> NotImplementedError
// after
with op.batch_alter_table('users') as batch_op:
batch_op.alter_column('email', nullable=True) Defensive patterns
Strategy: validation
Validate before calling
from sqlalchemy import create_engine
dialect_name = engine.dialect.name
if dialect_name in ("mysql", "mariadb") and not _in_batch():
raise RuntimeError("Use batch_alter_table for alter_column on MySQL/MariaDB") Type guard
def needs_batch_for_alter(dialect_name) -> bool:
return dialect_name in ("mysql", "mariadb") Prevention
- Default to batch_alter_table for any alter_column on MySQL.
- Run the test suite against the same dialect as production.
- When autogenerating on MySQL, set the batch flag so MODIFY is emitted.
When it happens
Trigger: Calling op.alter_column('t', 'c', nullable=True) (or server_default=, new_column_name via ColumnName, or type_=) directly on MySQL/MariaDB outside of batch mode, where the operation translates into one of the disallowed individual ColumnNullable/ColumnDefault/ColumnType constructs.
Common situations: A migration that worked on PostgreSQL is run against MySQL; auto-generated alter_column on MySQL without batching; combining type change + nullability in separate calls.
Related errors
- No generic 'DROP CONSTRAINT' in MySQL - please specify const
- No support for ALTER of constraints in SQLite dialect. Pleas
- The %s method does not apply to a batch table alter operatio
- recreate may be one of 'auto', 'always', or 'never'.
- Can't create table in batch mode
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/685fe800ad6f81ba.json.
Report an issue: GitHub.