sqlalchemy/alembic · error · ValueError
No such index
Error message
No such index: '%s'
What it means
Raised as ValueError by ApplyBatchImpl.drop_index (batch.py:706-710) after a KeyError when idx.name is not present in self.indexes (the dict of indexes tracked on the batch-recreated table). Batch mode needs to know which index to omit from the new table, so an unknown index name is an error, not a no-op.
Solutions
- Confirm the index name via the DB catalog (\di in psql, PRAGMA index_list in SQLite) and pass the exact name.
- If you are creating and immediately dropping an index in one batch, simply omit both calls.
- If the drop may not apply in all environments, inspect the batch table's indexes before calling drop_index.
Example fix
// before
with op.batch_alter_table('users') as batch_op:
batch_op.drop_index('ix_user_email') # typo / wrong name
// after
with op.batch_alter_table('users') as batch_op:
batch_op.drop_index('ix_users_email') # correct name Defensive patterns
Strategy: validation
Validate before calling
# Confirm an index exists before dropping it in batch mode.
def index_exists(conn, table, index_name):
return conn.exec_driver_sql(
"SELECT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = %s)",
(index_name,),
).scalar()
if index_exists(bind, 'users', 'ix_users_email'):
with op.batch_alter_table('users') as batch_op:
batch_op.drop_index('ix_users_email') Type guard
def is_known_index(name: str, known: set[str]) -> bool:
return name in known Try / catch
try:
with op.batch_alter_table('users') as batch_op:
batch_op.drop_index('maybe_missing')
except ValueError as e:
if 'No such index' in str(e):
pass # already absent; idempotent
else:
raise Prevention
- Query the DB catalog (pg_indexes / PRAGMA index_list) for the exact index name first.
- Use consistent index names across environments via naming_convention.
- Avoid dropping an index you did not confirm exists.
When it happens
Trigger: Calling batch_op.drop_index('idx_missing') (or batch_op.drop_index(sa.Index('idx_missing'))) inside a batch_alter_table block where no such index exists on the table. Common with a typo'd index name or an index that belongs to a different table.
Common situations: Auto-generated migration references an index name that was renamed/already dropped; index names that differ between dev (SQLite) and prod (Postgres); dropping an index created in the same batch but with a different name string.
Related errors
- Can't drop table in batch mode
- Constraint must have a name
- No such constraint
- TODO
- Connection, url, or dialect_name is required.
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/f9b86f2e7261b72b.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/operations/batch.py:710
# 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 5551b5d35f)