{"record":{"id":"c6e9e47ef4273c1d","repo":"sqlalchemy/alembic","slug":"no-such-constraint-s","errorCode":null,"errorMessage":"No such constraint: '%s'","messagePattern":"No such constraint: '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"alembic/operations/batch.py","lineNumber":697,"sourceCode":"            if const.name in self.col_named_constraints:\n                col, const = self.col_named_constraints.pop(const.name)\n\n                for col_const in list(self.columns[col.name].constraints):\n                    if col_const.name == const.name:\n                        self.columns[col.name].constraints.remove(col_const)\n            elif constraint_name_string(const.name):\n                const = self.named_constraints.pop(const.name)\n            elif const in self.unnamed_constraints:\n                self.unnamed_constraints.remove(const)\n\n        except KeyError:\n            if _is_type_bound(const):\n                # type-bound constraints are only included in the new\n                # table via their type object in any case, so ignore the\n                # drop_constraint() that comes here via the\n                # Operations.implementation_for(alter_column)\n                return\n            raise ValueError(\"No such constraint: '%s'\" % const.name)\n        else:\n            if isinstance(const, PrimaryKeyConstraint):\n                for col in const.columns:\n                    self.columns[col.name].primary_key = False\n\n    def create_index(self, idx: Index) -> None:\n        self.new_indexes[idx.name] = idx  # type: ignore[index]\n\n    def drop_index(self, idx: Index) -> None:\n        try:\n            del self.indexes[idx.name]  # type: ignore[arg-type]\n        except KeyError:\n            raise ValueError(\"No such index: '%s'\" % idx.name)\n\n    def rename_table(self, *arg, **kw):\n        raise NotImplementedError(\"TODO\")\n","sourceCodeStart":679,"sourceCodeEnd":714,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/operations/batch.py#L679-L714","documentation":"Raised as ValueError by ApplyBatchImpl.drop_constraint (batch.py:697) after a KeyError when a named constraint is not found in col_named_constraints, named_constraints, or unnamed_constraints, AND the constraint is not a type-bound constraint. The guard at line 691 silently ignores type-bound constraints (they live on the type object), but any other missing name is a real error because batch mode needs to drop it from the recreated table.","triggerScenarios":"batch_op.drop_constraint('does_not_exist', type_='unique') inside a batch_alter_table block where no constraint of that name exists on the table being recreated. Also when the constraint name was typo'd, lives on a different table, or was already dropped earlier in the same batch.","commonSituations":"Migration written against one branch's schema but applied to a DB at a different revision; constraint names that differ between environments (e.g. auto-generated names like users_email_key vs an explicit name); ordering two drop_constraint calls for the same name.","solutions":["Verify the constraint exists: query the DB (e.g. SELECT conname FROM pg_constraint) and use the exact name.","Check whether an earlier operation in the same batch or a prior revision already dropped it.","If the drop is optional, guard it: first inspect via batch reflection or run a conditional drop only if the name resolves."],"exampleFix":"// before\nwith op.batch_alter_table('users') as batch_op:\n    batch_op.drop_constraint('users_email_key', type_='unique')  # wrong name\n// after\nwith op.batch_alter_table('users') as batch_op:\n    batch_op.drop_constraint('uq_users_email', type_='unique')  # actual name","handlingStrategy":"validation","validationCode":"# Verify a constraint name exists on the table before dropping it in batch mode.\ndef constraint_exists(conn, table, name):\n    return conn.exec_driver_sql(\n        \"SELECT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = %s)\",\n        (name,),\n    ).scalar()\n\nif constraint_exists(bind, 'users', 'uq_users_email'):\n    with op.batch_alter_table('users') as batch_op:\n        batch_op.drop_constraint('uq_users_email', type_='unique')","typeGuard":"def is_known_constraint(name: str, known: set[str]) -> bool:\n    return name in known","tryCatchPattern":"from alembic.util import exc as alembic_exc\n\ntry:\n    with op.batch_alter_table('users') as batch_op:\n        batch_op.drop_constraint('maybe_missing', type_='unique')\nexcept ValueError as e:\n    if 'No such constraint' in str(e):\n        # constraint already gone; treat as success for idempotent migrations\n        pass\n    else:\n        raise","preventionTips":["Introspect the DB catalog for the exact constraint name before batch drop.","Make constraint names deterministic via a naming_convention.","Watch for double-drops within the same batch."],"tags":["alembic","batch-mode","constraints","database-migration"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}