sqlalchemy/alembic · error · NotImplementedError
No support for ALTER of constraints in SQLite dialect. Pleas
Error message
No support for ALTER of constraints in SQLite dialect. Please refer to the batch mode feature which allows for SQLite migrations using a copy-and-move strategy.
What it means
SQLite cannot ALTER TABLE to add most constraints (it lacks ADD CONSTRAINT). Alembic's SQLite dialect distinguishes implicit constraints (which it warns and skips) from explicit ones (which have _create_rule set to None and would produce invalid SQL); explicit adds raise with a pointer to batch mode, which recreates the table via copy-and-move.
Source
Thrown at alembic/ddl/sqlite.py:78
if isinstance(
col.server_default, schema.DefaultClause
) and isinstance(col.server_default.arg, sql.ClauseElement):
return True
elif (
isinstance(col.server_default, Computed)
and col.server_default.persisted
):
return True
elif op[0] not in ("create_index", "drop_index"):
return True
else:
return False
def add_constraint(self, const: Constraint, **kw: Any):
# attempt to distinguish between an
# auto-gen constraint and an explicit one
if const._create_rule is None:
raise NotImplementedError(
"No support for ALTER of constraints in SQLite dialect. "
"Please refer to the batch mode feature which allows for "
"SQLite migrations using a copy-and-move strategy."
)
elif const._create_rule(self):
util.warn(
"Skipping unsupported ALTER for "
"creation of implicit constraint. "
"Please refer to the batch mode feature which allows for "
"SQLite migrations using a copy-and-move strategy."
)
def drop_constraint(self, const: Constraint, **kw: Any):
if const._create_rule is None:
raise NotImplementedError(
"No support for ALTER of constraints in SQLite dialect. "
"Please refer to the batch mode feature which allows for "
"SQLite migrations using a copy-and-move strategy."View on GitHub (pinned to 44fb345033)
Solutions
- Wrap the operation in op.batch_alter_table('t') as batch_op: and use batch_op.create_foreign_key(...) etc.
- For new tables, declare the constraint inline in the CreateTable operation instead of adding it later.
- Set recreate='always' in batch_alter_table if auto-detection misfires.
Example fix
// before
op.create_foreign_key('fk_t_u', 't', 'u', ['u_id'], ['id']) # SQLite -> NotImplementedError
// after
with op.batch_alter_table('t') as batch_op:
batch_op.create_foreign_key('fk_t_u', 't', 'u', ['u_id'], ['id']) Defensive patterns
Strategy: validation
Validate before calling
if engine.dialect.name == "sqlite":
if not _in_batch():
raise RuntimeError("SQLite cannot add constraints without batch mode") Type guard
def is_sqlite(dialect) -> bool:
return getattr(dialect, "name", "") == "sqlite" Prevention
- Wrap any create_*_constraint in batch_alter_table on SQLite.
- Declare constraints inline in CreateTable for SQLite when possible.
- Run migration tests against SQLite to catch these early.
When it happens
Trigger: Calling op.create_foreign_key(...), op.create_unique_constraint(...), op.create_check_constraint(...), or op.create_primary_key(...) on SQLite outside of batch mode.
Common situations: A migration authored on PostgreSQL uses op.create_foreign_key and is run against SQLite in tests; a SQLite-first project that tries to add a constraint to an existing table.
Related errors
- Constraint must have a name
- No such constraint: '%s'
- Individual alter column constructs not supported by MySQL
- No generic 'DROP CONSTRAINT' in MySQL - please specify const
- The %s method does not apply to a batch table alter operatio
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/3620ecdfc4730c76.json.
Report an issue: GitHub.