sqlalchemy/alembic · error · ValueError

No such index: '%s'

Error message

No such index: '%s'

What it means

Raised by ApplyBatchImpl.drop_index() when del self.indexes[idx.name] raises KeyError. The index name supplied to batch_op.drop_index() is not among the indexes reflected/copied from the target table, so batch mode cannot remove it from the rebuild plan.

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 44fb345033)

Solutions

  1. Reflect the table's actual indexes and use the real name.
  2. Verify the index has not already been dropped earlier in this migration or a prior revision.
  3. Add an existence guard before calling batch_op.drop_index().

Example fix

// before
with op.batch_alter_table('user') as batch_op:
    batch_op.drop_index('ix_user_nonexistent')  # raises ValueError

// after
from sqlalchemy import inspect
index_names = [i['name'] for i in inspect(op.get_bind()).get_indexes('user')]
with op.batch_alter_table('user') as batch_op:
    if 'ix_user_email' in index_names:
        batch_op.drop_index('ix_user_email')
Defensive patterns

Strategy: validation

Validate before calling

from sqlalchemy import inspect

def index_exists(bind, table, name) -> bool:
    return name in {i['name'] for i in inspect(bind).get_indexes(table)}

Try / catch

try:
    batch_op.drop_index(name)
except ValueError as e:
    if 'No such index' in str(e):
        pass  # already absent
    else:
        raise

Prevention

When it happens

Trigger: Inside batch_alter_table, calling batch_op.drop_index('idx_name') where 'idx_name' is not an index on the table; dropping an index already dropped; index name typo or quoting/case mismatch.

Common situations: Migration referencing an index removed in another branch/revision; index named via naming convention in metadata but reflected under a different name; running migrations out of order against a DB whose state doesn't match.

Related errors


AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04). Data as JSON: /data/errors/f9b86f2e7261b72b.json. Report an issue: GitHub.