sqlalchemy/alembic · error · ValueError
constraint cannot be produced; original constraint is not pr
Error message
constraint cannot be produced; original constraint is not present
What it means
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.
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 44fb345033)
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().
Example fix
// before
op_obj = DropConstraintOp('uq_user_email', 'user')
op_obj.to_constraint() # raises ValueError
// after
from sqlalchemy import UniqueConstraint
op_obj = DropConstraintOp.from_constraint(
UniqueConstraint(__name__='uq_user_email')
)
op_obj.to_constraint() # ok Defensive patterns
Strategy: validation
Validate before calling
from alembic.operations.ops import DropConstraintOp
def build_reversible_drop(constraint):
# from_constraint populates _reverse so to_constraint()/reverse() work
return DropConstraintOp.from_constraint(constraint) Type guard
from alembic.operations.ops import DropConstraintOp
def drop_is_reversible(op_obj: DropConstraintOp) -> bool:
return getattr(op_obj, '_reverse', None) is not None Try / catch
try:
op_obj.to_constraint()
except ValueError as e:
if 'original constraint is not present' in str(e):
op_obj = DropConstraintOp.from_constraint(real_constraint)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- operation is not reversible; original column is not present
- 'type' can be one of %s
- Constraint must have a name
- No such constraint: '%s'
- This MigrationScript instance has a multiple-entry list for
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/5ecad8452431f4e4.json.
Report an issue: GitHub.