sqlalchemy/alembic · error · NotImplementedError
No support for ALTER of constraints in SQLite dialect…
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
SQLiteImpl.add_constraint raises NotImplementedError because SQLite does not support ALTER TABLE ADD CONSTRAINT for foreign keys, unique constraints, checks, or primary keys after table creation. Alembic directs you to batch mode, which recreates the table with the new constraint using a copy-and-move strategy.
Solutions
- Wrap the operation in batch mode: with op.batch_alter_table('t') as batch_op: batch_op.create_foreign_key(...).
- Set recreate='always' in batch_alter_table when the constraint change is not auto-detected as needing a rebuild.
- Design test SQLite schemas to match constraints at table-creation time to avoid needing ALTER.
Example fix
# before (fails on SQLite)
op.create_foreign_key('fk_a_b', 'a', 'b', ['b_id'], ['id'])
# after
with op.batch_alter_table('a', schema=None) as batch_op:
batch_op.create_foreign_key('fk_a_b', 'b', ['b_id'], ['id']) Defensive patterns
Strategy: validation
Validate before calling
def add_constraint_sqlite_safe(op, table_name, fn):
with op.batch_alter_table(table_name) as batch_op:
fn(batch_op) Type guard
from sqlalchemy.dialects import sqlite
def dialect_needs_batch(dialect) -> bool:
return dialect.name == 'sqlite' Prevention
- Always use batch_alter_table for constraint changes on SQLite.
- Run your migration suite against SQLite in CI to detect non-batched constraint ops.
- Set recreate='always' when the auto heuristic does not detect the rebuild need.
When it happens
Trigger: Calling op.create_foreign_key(), op.create_unique_constraint(), op.create_check_constraint(), or op.create_primary_key() against a SQLite database outside of a batch_alter_table() context.
Common situations: Running migrations developed against PostgreSQL/MySQL on a SQLite test database; CI using in-memory SQLite where migrations assume full ALTER support.
Related errors
- Can't create table in batch mode
- Individual alter column constructs not supported by MySQL
- No generic 'DROP CONSTRAINT' in MySQL - please specify…
- recreate may be one of 'auto', 'always', or 'never'.
- The method does not apply to a batch table alter operation.
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/3620ecdfc4730c76.
Report an issue: GitHub.
Appendix: 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 5551b5d35f)