{"id":"b48faa1b6f493a97","repo":"sqlalchemy/alembic","slug":"sqlalchemy-2-0-required","errorCode":null,"errorMessage":"SQLAlchemy 2.0 required","messagePattern":"SQLAlchemy 2\\.0 required","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"alembic/operations/toimpl.py","lineNumber":233,"sourceCode":"\n\n@Operations.implementation_for(ops.AddConstraintOp)\ndef create_constraint(\n    operations: \"Operations\", operation: \"ops.AddConstraintOp\"\n) -> None:\n    operations.impl.add_constraint(\n        operation.to_constraint(operations.migration_context)\n    )\n\n\n@Operations.implementation_for(ops.DropConstraintOp)\ndef drop_constraint(\n    operations: \"Operations\", operation: \"ops.DropConstraintOp\"\n) -> None:\n    kw = {}\n    if operation.if_exists is not None:\n        if not sqla_2:\n            raise NotImplementedError(\"SQLAlchemy 2.0 required\")\n        kw[\"if_exists\"] = operation.if_exists\n    operations.impl.drop_constraint(\n        operations.schema_obj.generic_constraint(\n            operation.constraint_name,\n            operation.table_name,\n            operation.constraint_type,\n            schema=operation.schema,\n        ),\n        **kw,\n    )\n\n\n@Operations.implementation_for(ops.BulkInsertOp)\ndef bulk_insert(\n    operations: \"Operations\", operation: \"ops.BulkInsertOp\"\n) -> None:\n    operations.impl.bulk_insert(  # type: ignore[union-attr]\n        operation.table, operation.rows, multiinsert=operation.multiinsert","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/44fb3450330204b222ff05135e1fbbbdb28c44db/alembic/operations/toimpl.py#L215-L251","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (SQLAlchemy 1.4 installed)\nop.drop_constraint('uq_user_email', 'user', if_exists=True)  # raises NotImplementedError\n\n// after\nsqlalchemy>=2.0 in requirements, then:\nop.drop_constraint('uq_user_email', 'user', if_exists=True)","handlingStrategy":"validation","validationCode":"from alembic.util.sqla_compat import sqla_2\n\ndef can_use_if_exists() -> bool:\n    return bool(sqla_2)","typeGuard":null,"tryCatchPattern":"from alembic.util.sqla_compat import sqla_2\nif if_exists and not sqla_2:\n    # guard manually instead of relying on if_exists\n    if constraint_exists(bind, table, name):\n        op.drop_constraint(name, table)\nelse:\n    op.drop_constraint(name, table, if_exists=if_exists)","preventionTips":["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."],"tags":["alembic","sqlalchemy-version","dependencies","migrations"],"analyzedSha":"44fb3450330204b222ff05135e1fbbbdb28c44db","analyzedAt":"2026-08-04T19:57:10.248Z","schemaVersion":2}