sqlalchemy/alembic · error · ValueError
constraint cannot be produced; original constraint is not…
Error message
constraint cannot be produced; original constraint is not present
What it means
Raised as ValueError by DropConstraintOp.to_constraint() (ops.py:177-190) when the operation's _reverse attribute is None. The reverse reference is set only when DropConstraintOp is built via from_constraint(), which captures the original Constraint object so to_constraint() can reconstruct it. A manually constructed DropConstraintOp (no _reverse) cannot materialise a Constraint, so the call fails.
Solutions
- Build the op via the classmethod: DropConstraintOp.from_constraint(existing_constraint_obj), which sets _reverse automatically.
- If you only have a name, construct the Constraint yourself with schema_obj.generic_constraint(name, table, type_) instead of calling to_constraint() on a bare op.
- Avoid calling to_constraint() on ops created from a plain name; guard with `if op._reverse is not None:` before invoking.
Example fix
// before
op = ops.DropConstraintOp(constraint_name='ck', table_name='t', type_='check')
constraint = op.to_constraint() # raises
// after
constraint = operations.schema_obj.generic_constraint('ck', 't', 'check')
# or, if you have the original:
op = ops.DropConstraintOp.from_constraint(original_constraint)
constraint = op.to_constraint() Defensive patterns
Strategy: validation
Validate before calling
# Build DropConstraintOp via from_constraint so _reverse is populated.
from alembic.operations.ops import DropConstraintOp
def safe_to_constraint(drop_op):
if drop_op._reverse is None:
raise ValueError(
'DropConstraintOp has no reverse; build via DropConstraintOp.from_constraint()'
)
return drop_op.to_constraint()
# preferred construction:
drop_op = DropConstraintOp.from_constraint(original_constraint)
constraint = drop_op.to_constraint() Type guard
from alembic.operations.ops import DropConstraintOp
def has_reverse(op: DropConstraintOp) -> bool:
return getattr(op, '_reverse', None) is not None Prevention
- Always construct DropConstraintOp with from_constraint() when you later need to_constraint().
- For name-only drops, use schema_obj.generic_constraint() rather than to_constraint() on a bare op.
- Guard custom loops with `if op._reverse is not None`.
When it happens
Trigger: Directly instantiating ops.DropConstraintOp(constraint_name='ck', table_name='t', type_='check') without passing _reverse, then calling .to_constraint() on it. This typically happens in custom operation code or when manipulating operation objects in autogenerate hooks rather than going through from_constraint().
Common situations: Third-party tooling or custom MigrateOperation subclasses that build DropConstraintOp by hand instead of using the from_constraint() classmethod. Also when calling op.drop_constraint(...) with only a name string (no constraint object), then introspecting the returned op.
Related errors
- Constraint must have a name
- No such constraint
- operation is not reversible; original column is not present
- 'type' can be one of
- Can't drop table in batch mode
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/5ecad8452431f4e4.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/operations/ops.py:187
return cls(
sqla_compat.constraint_name_or_none(constraint.name),
constraint_table.name,
schema=constraint_table.schema,
type_=types.get(constraint.__visit_name__),
_reverse=AddConstraintOp.from_constraint(constraint),
)
def to_constraint(self) -> Constraint:
if self._reverse is not None:
constraint = self._reverse.to_constraint()
constraint.name = self.constraint_name
constraint_table = sqla_compat._table_for_constraint(constraint)
constraint_table.name = self.table_name
constraint_table.schema = self.schema
return constraint
else:
raise ValueError(
"constraint cannot be produced; "
"original constraint is not present"
)
@classmethod
def drop_constraint(
cls,
operations: Operations,
constraint_name: str,
table_name: str,
type_: str | None = None,
*,
schema: str | None = None,
if_exists: bool | None = None,
) -> None:
r"""Drop a constraint of the given name, typically via DROP CONSTRAINT.
:param constraint_name: name of the constraint.View on GitHub (pinned to 5551b5d35f)