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 compilation of ColumnNullable, ColumnName, ColumnDefault, or ColumnType raises NotImplementedError. MySQL does not support individual ALTER COLUMN sub-operations; it requires a single CHANGE COLUMN or MODIFY COLUMN that restates the full column definition. Alembic emits MySQL-specific constructs (MySQLModifyColumn) instead, so hitting the generic ones means an unsupported path was reached.
Solutions
- Use op.alter_column() which Alembic translates to CHANGE/MODIFY COLUMN on MySQL, restating nullable/type/default together.
- When altering a MySQL column, supply existing_type (and existing_nullable/existing_server_default) so the full MODIFY spec can be generated.
- Avoid compiling bare ColumnNullable/ColumnType constructs directly for the MySQL dialect.
Example fix
# before (unsupported on MySQL)
# a bare alter that loses full column info
# after: alter_column restates full definition
op.alter_column('t', 'c', existing_type=sa.Integer(),
type_=sa.String(50), nullable=False) Defensive patterns
Strategy: validation
Validate before calling
def alter_column_mysql(op, table, col, existing_type, **kw):
# always pass existing_type so MODIFY COLUMN can restate the definition
op.alter_column(table, col, existing_type=existing_type, **kw) Type guard
def has_existing_type(kw: dict) -> bool:
return kw.get('existing_type') is not None Prevention
- Always supply existing_type when calling op.alter_column on MySQL/MariaDB.
- Let Alembic's MySQL impl translate to CHANGE/MODIFY COLUMN rather than building alter constructs manually.
- Never compile bare ColumnNullable/ColumnType constructs for the MySQL dialect.
When it happens
Trigger: Custom code that directly compiles a bare ColumnNullable/ColumnName/ColumnDefault/ColumnType against the mysql or mariadb dialect; or an operations path that bypasses Alembic's alter_column MySQL rewrite and feeds the generic construct to the compiler.
Common situations: Third-party dialect plugins or custom ops that build standalone alter-column constructs; calling op.alter_column with a combination that Alembic's MySQL impl fails to translate into MySQLChangeColumn.
Related errors
- No generic 'DROP CONSTRAINT' in MySQL - please specify…
- No support for ALTER of constraints in SQLite dialect…
- Can't create table in batch mode
- Can't send params and multiparams at the same time
- Cannot call run_async with a sync engine
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/685fe800ad6f81ba.
Report an issue: GitHub.
Appendix: 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 5551b5d35f)