{"id":"5ecad8452431f4e4","repo":"sqlalchemy/alembic","slug":"constraint-cannot-be-produced-original-constraint","errorCode":null,"errorMessage":"constraint cannot be produced; original constraint is not present","messagePattern":"constraint cannot be produced; original constraint is not present","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"alembic/operations/ops.py","lineNumber":187,"sourceCode":"        return cls(\n            sqla_compat.constraint_name_or_none(constraint.name),\n            constraint_table.name,\n            schema=constraint_table.schema,\n            type_=types.get(constraint.__visit_name__),\n            _reverse=AddConstraintOp.from_constraint(constraint),\n        )\n\n    def to_constraint(self) -> Constraint:\n        if self._reverse is not None:\n            constraint = self._reverse.to_constraint()\n            constraint.name = self.constraint_name\n            constraint_table = sqla_compat._table_for_constraint(constraint)\n            constraint_table.name = self.table_name\n            constraint_table.schema = self.schema\n\n            return constraint\n        else:\n            raise ValueError(\n                \"constraint cannot be produced; \"\n                \"original constraint is not present\"\n            )\n\n    @classmethod\n    def drop_constraint(\n        cls,\n        operations: Operations,\n        constraint_name: str,\n        table_name: str,\n        type_: str | None = None,\n        *,\n        schema: str | None = None,\n        if_exists: bool | None = None,\n    ) -> None:\n        r\"\"\"Drop a constraint of the given name, typically via DROP CONSTRAINT.\n\n        :param constraint_name: name of the constraint.","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/44fb3450330204b222ff05135e1fbbbdb28c44db/alembic/operations/ops.py#L169-L205","documentation":"Raised by DropConstraintOp.to_constraint() when self._reverse is None. to_constraint() reconstructs the original constraint object from the reverse (AddConstraintOp) captured when the op was built via from_constraint(); a DropConstraintOp constructed directly has no reverse and cannot materialize a constraint. This is hit when code or the autogenerate reverser tries to render/emit a constraint from a manually-built drop op.","triggerScenarios":"Constructing DropConstraintOp(name, table) directly (not via from_constraint/DropConstraintOp.drop_constraint) and then calling .to_constraint(), .to_diff_tuple(), or .reverse() on it; a custom Rewriter/process_revision_directives that builds drop ops without the reverse linkage.","commonSituations":"Custom autogenerate hooks or migration frameworks that instantiate DropConstraintOp manually; reversing a drop op that was created from a bare name rather than from a real Constraint object.","solutions":["Build the DropConstraintOp via DropConstraintOp.from_constraint(constraint) so _reverse is populated, instead of constructing it directly.","Use the high-level op.drop_constraint() API which constructs the op without _reverse for normal execution (to_constraint is only needed for reverse/diff paths).","In custom rewriter code, ensure each drop op carries its reverse AddConstraintOp before invoking reverse()/to_diff_tuple()."],"exampleFix":"// before\nop_obj = DropConstraintOp('uq_user_email', 'user')\nop_obj.to_constraint()  # raises ValueError\n\n// after\nfrom sqlalchemy import UniqueConstraint\nop_obj = DropConstraintOp.from_constraint(\n    UniqueConstraint(__name__='uq_user_email')\n)\nop_obj.to_constraint()  # ok","handlingStrategy":"validation","validationCode":"from alembic.operations.ops import DropConstraintOp\n\ndef build_reversible_drop(constraint):\n    # from_constraint populates _reverse so to_constraint()/reverse() work\n    return DropConstraintOp.from_constraint(constraint)","typeGuard":"from alembic.operations.ops import DropConstraintOp\ndef drop_is_reversible(op_obj: DropConstraintOp) -> bool:\n    return getattr(op_obj, '_reverse', None) is not None","tryCatchPattern":"try:\n    op_obj.to_constraint()\nexcept ValueError as e:\n    if 'original constraint is not present' in str(e):\n        op_obj = DropConstraintOp.from_constraint(real_constraint)\n    else:\n        raise","preventionTips":["Build drop ops with from_constraint() when reversibility is needed.","Avoid constructing DropConstraintOp directly in custom autogenerate hooks.","Use the high-level op.drop_constraint() for normal execution."],"tags":["alembic","operations","constraints","migrations","internals"],"analyzedSha":"44fb3450330204b222ff05135e1fbbbdb28c44db","analyzedAt":"2026-08-04T19:57:10.248Z","schemaVersion":2}