sqlalchemy/alembic · error · NotImplementedError
SQLAlchemy 2.0 required
Error message
SQLAlchemy 2.0 required
What it means
Raised by the drop_constraint implementation when operation.if_exists is set but sqla_2 is False (SQLAlchemy < 2.0 installed). The IF EXISTS clause for DROP CONSTRAINT was only added to Alembic's path via SQLAlchemy 2.0 facilities, so requesting if_exists=True on an older SQLAlchemy is unsupported.
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 44fb345033)
Solutions
- Upgrade SQLAlchemy to >= 2.0.
- Remove the if_exists=True argument and guard the drop manually with an existence check instead.
- Pin the migration's requirements so SQLAlchemy >= 2.0 is guaranteed at runtime.
Example fix
// before (SQLAlchemy 1.4 installed)
op.drop_constraint('uq_user_email', 'user', if_exists=True) # raises NotImplementedError
// after
sqlalchemy>=2.0 in requirements, then:
op.drop_constraint('uq_user_email', 'user', if_exists=True) Defensive patterns
Strategy: validation
Validate before calling
from alembic.util.sqla_compat import sqla_2
def can_use_if_exists() -> bool:
return bool(sqla_2) Try / catch
from alembic.util.sqla_compat import sqla_2
if if_exists and not sqla_2:
# guard manually instead of relying on if_exists
if constraint_exists(bind, table, name):
op.drop_constraint(name, table)
else:
op.drop_constraint(name, table, if_exists=if_exists) Prevention
- Pin SQLAlchemy >= 2.0 in requirements when using if_exists features.
- Guard if_exists usage behind a sqla_2 capability check.
- Prefer manual existence checks in mixed-version environments.
When it happens
Trigger: Calling op.drop_constraint('c','t', if_exists=True) while SQLAlchemy < 2.0 is installed; setting if_exists on a DropConstraintOp in an environment pinned to SQLAlchemy 1.4.
Common situations: Pinning an older SQLAlchemy for compatibility with legacy apps; CI using a different SQLAlchemy version than production; copy-pasting an if_exists=True call from a 2.0-only codebase.
Related errors
- Can't drop table in batch mode
- Constraint must have a name
- No such constraint: '%s'
- No such index: '%s'
- constraint cannot be produced; original constraint is not pr
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/b48faa1b6f493a97.json.
Report an issue: GitHub.