sqlalchemy/alembic · error · ValueError
No such constraint: '%s'
Error message
No such constraint: '%s'
What it means
Raised by ApplyBatchImpl.drop_constraint() after a KeyError while looking up the constraint name. The name is not present in named_constraints, col_named_constraints or unnamed_constraints, and the constraint is not type-bound (type-bound drops are silently ignored). This means the constraint being dropped does not exist on the table that batch mode reflected or was copied from.
Source
Thrown at alembic/operations/batch.py:697
if const.name in self.col_named_constraints:
col, const = self.col_named_constraints.pop(const.name)
for col_const in list(self.columns[col.name].constraints):
if col_const.name == const.name:
self.columns[col.name].constraints.remove(col_const)
elif constraint_name_string(const.name):
const = self.named_constraints.pop(const.name)
elif const in self.unnamed_constraints:
self.unnamed_constraints.remove(const)
except KeyError:
if _is_type_bound(const):
# type-bound constraints are only included in the new
# table via their type object in any case, so ignore the
# drop_constraint() that comes here via the
# Operations.implementation_for(alter_column)
return
raise ValueError("No such constraint: '%s'" % const.name)
else:
if isinstance(const, PrimaryKeyConstraint):
for col in const.columns:
self.columns[col.name].primary_key = False
def create_index(self, idx: Index) -> None:
self.new_indexes[idx.name] = idx # type: ignore[index]
def drop_index(self, idx: Index) -> None:
try:
del self.indexes[idx.name] # type: ignore[arg-type]
except KeyError:
raise ValueError("No such index: '%s'" % idx.name)
def rename_table(self, *arg, **kw):
raise NotImplementedError("TODO")
View on GitHub (pinned to 44fb345033)
Solutions
- Inspect the live table for actual constraint names (e.g. via reflection / PRAGMA foreign_key_list / information_schema) and correct the name in the migration.
- Confirm the constraint has not already been dropped by a prior revision or earlier in the same batch.
- Guard the drop with an existence check or use if_exists semantics where supported.
- If the constraint is type-bound, drop it via alter_column on the column type rather than drop_constraint.
Example fix
// before
with op.batch_alter_table('user') as batch_op:
batch_op.drop_constraint('fk_user_old') # not present -> ValueError
// after
# verify against the reflected table first
from sqlalchemy import inspect
constraints = [c['name'] for c in inspect(op.get_bind()).get_foreign_keys('user')]
with op.batch_alter_table('user') as batch_op:
if 'fk_user_old' in constraints:
batch_op.drop_constraint('fk_user_old') Defensive patterns
Strategy: validation
Validate before calling
from sqlalchemy import inspect
def constraint_exists(bind, table, name) -> bool:
fks = [c['name'] for c in inspect(bind).get_foreign_keys(table)]
uqs = [c['name'] for c in inspect(bind).get_unique_constraints(table)]
cks = [c['name'] for c in inspect(bind).get_check_constraints(table)]
return name in (set(fks) | set(uqs) | set(cks)) Try / catch
try:
batch_op.drop_constraint(name)
except ValueError as e:
if 'No such constraint' in str(e):
pass # already absent, treat as success or log
else:
raise Prevention
- Reflect the table and check constraint names before dropping.
- Make migrations idempotent by guarding drops with existence checks.
- Watch for constraints already removed in prior revisions.
When it happens
Trigger: Inside batch_alter_table, calling batch_op.drop_constraint('some_name') where 'some_name' is not a constraint on the target table; dropping a constraint that was already dropped in the same batch; dropping a constraint whose name was changed/never existed; case/quoting mismatch in the constraint name.
Common situations: Stale migration referencing a constraint that a later/earlier revision already removed; typo in constraint name; mismatch between the name in metadata and the name reflected from the DB (e.g. naming convention differences); running a migration against a DB that is ahead of or behind the expected state.
Related errors
- Constraint must have a name
- Can't drop table in batch mode
- No such index: '%s'
- constraint cannot be produced; original constraint is not pr
- 'type' can be one of %s
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/c6e9e47ef4273c1d.json.
Report an issue: GitHub.