sqlalchemy/alembic · error · NotImplementedError
SQLAlchemy 2.0 required
Error message
SQLAlchemy 2.0 required
What it means
Raised as NotImplementedError('SQLAlchemy 2.0 required') by the DropConstraintOp implementation (toimpl.py:226-233) when the operation's if_exists argument is set and the installed SQLAlchemy is older than 2.0. The `if_exists` kwarg for DROP CONSTRAINT relies on SQLAlchemy 2.0+ DDL features; sqla_2 (sqla_compat.py:69) gates it, so pre-2.0 installs cannot honour the flag.
Solutions
- Upgrade SQLAlchemy to >= 2.0 (pip install 'SQLAlchemy>=2.0').
- Remove the if_exists=True argument and handle existence yourself (e.g. query the catalog first), keeping SQLAlchemy 1.4.
- Pin Alembic to a version compatible with your SQLAlchemy 1.4 install if upgrading SQLAlchemy is not possible.
Example fix
// before (SQLAlchemy 1.4)
op.drop_constraint('ck_t', 't', type_='check', if_exists=True) # raises
// after
op.drop_constraint('ck_t', 't', type_='check') # SQLAlchemy 1.4 compatible Defensive patterns
Strategy: validation
Validate before calling
# Check SQLAlchemy version before using if_exists on drop_constraint.
import sqlalchemy
from alembic.util.sqla_compat import sqla_2
def drop_constraint_maybe(name, table, type_, if_exists=False):
if if_exists and not sqla_2:
raise RuntimeError(
f'drop_constraint(if_exists=True) needs SQLAlchemy>=2.0, got {sqlalchemy.__version__}'
)
op.drop_constraint(name, table, type_=type_, if_exists=if_exists if sqla_2 else None) Type guard
from alembic.util.sqla_compat import sqla_2
def supports_drop_if_exists() -> bool:
return sqla_2 Prevention
- Pin SQLAlchemy>=2.0 in requirements if you use if_exists.
- Gate if_exists behind a version check in shared migration code.
- Test migrations in CI against the minimum supported SQLAlchemy version.
When it happens
Trigger: Calling op.drop_constraint('ck', 't', type_='check', if_exists=True) while running SQLAlchemy < 2.0 (e.g. 1.4.x). The implementation_for function checks `if operation.if_exists is not None and not sqla_2` and raises before touching the DB.
Common situations: Projects pinned to SQLAlchemy 1.4 (legacy apps) that pick up a newer Alembic migration using if_exists; CI with an older SQLAlchemy than local dev; dependency resolver installing an old SQLAlchemy.
Related errors
- constraint cannot be produced; original constraint is not…
- Constraint must have a name
- No such constraint
- is not in the same subtree as
- 'type' can be one of
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/b48faa1b6f493a97.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/operations/toimpl.py:233
@Operations.implementation_for(ops.AddConstraintOp)
def create_constraint(
operations: "Operations", operation: "ops.AddConstraintOp"
) -> None:
operations.impl.add_constraint(
operation.to_constraint(operations.migration_context)
)
@Operations.implementation_for(ops.DropConstraintOp)
def drop_constraint(
operations: "Operations", operation: "ops.DropConstraintOp"
) -> None:
kw = {}
if operation.if_exists is not None:
if not sqla_2:
raise NotImplementedError("SQLAlchemy 2.0 required")
kw["if_exists"] = operation.if_exists
operations.impl.drop_constraint(
operations.schema_obj.generic_constraint(
operation.constraint_name,
operation.table_name,
operation.constraint_type,
schema=operation.schema,
),
**kw,
)
@Operations.implementation_for(ops.BulkInsertOp)
def bulk_insert(
operations: "Operations", operation: "ops.BulkInsertOp"
) -> None:
operations.impl.bulk_insert( # type: ignore[union-attr]
operation.table, operation.rows, multiinsert=operation.multiinsertView on GitHub (pinned to 5551b5d35f)