sqlalchemy/alembic · error · RangeNotAncestorError

Revision %s is not an ancestor of revision %s

Error message

Revision %s is not an ancestor of revision %s

What it means

Raised as RangeNotAncestorError inside _collect_downgrade_revisions (revision.py:1415) when a downgrade target revision set has an empty intersection with the currently active revisions. The message template 'Revision %s is not an ancestor of revision %s' is produced by RangeNotAncestorError.__init__; here the lower slot is the literal 'Nothing to drop'. It signals that the revisions you asked to downgrade away from are not actually present in the migration path leading to the requested heads.

Source

Thrown at alembic/script/revision.py:1415

        )

        # Emit revisions to drop in reverse topological sorted order.
        downgrade_revisions.intersection_update(active_revisions)

        if implicit_base:
            # Wind other branches back to base.
            downgrade_revisions.update(
                active_revisions.difference(self._get_ancestor_nodes(roots))
            )

        if (
            target_revision is not None
            and not downgrade_revisions
            and target_revision not in heads
        ):
            # Empty intersection: target revs are not present.

            raise RangeNotAncestorError("Nothing to drop", upper)

        return downgrade_revisions, heads

    def _collect_upgrade_revisions(
        self,
        upper: _RevisionIdentifierType,
        lower: _RevisionIdentifierType,
        inclusive: bool,
        implicit_base: bool,
        assert_relative_length: bool,
    ) -> tuple[set[Revision], tuple[Revision, ...]]:
        """
        Compute the set of required revisions specified by :upper, and the
        current set of active revisions specified by :lower. Find the
        difference between the two to compute the required upgrades.

        :inclusive=True includes the current/lower revisions in the set

View on GitHub (pinned to 44fb345033)

Solutions

  1. Run `alembic history --verbose` and confirm both the current DB revision (`alembic current`) and the target revision are on the same branch line.
  2. Ensure the downgrade target is a genuine ancestor of the current head you are rolling back from.
  3. If branches are involved, disambiguate with the branch label (e.g. branchname@revision) so the correct lineage is selected.
  4. If migration files were rebased/renamed, align the database alembic_version row with the actual revision id before retrying.

Example fix

# before: target 'abc123' is on a sibling branch, not an ancestor of current head
alembic downgrade abc123
# after: downgrade along the correct branch lineage
alembic downgrade branchname@abc123
Defensive patterns

Strategy: validation

Validate before calling

from alembic.script import ScriptDirectory
from alembic.script.revision import RangeNotAncestorError

sd = ScriptDirectory.from_config(cfg)
current = sd.get_heads()  # or the DB's current revision
target = 'abc123'
rev_map = sd.get_revision_map()
ancestors = set(r.revision for r in rev_map._get_ancestor_nodes([rev_map.get_revision(target)]))
if not any(c in ancestors for c in current):
    raise SystemExit(f'{target} is not an ancestor-descendant of current {current}')

Try / catch

from alembic.script.revision import RangeNotAncestorError
try:
    command.downgrade(cfg, target)
except RangeNotAncestorError as e:
    print(f'Cannot downgrade: {e}. Verify branch lineage with `alembic history`.')
    # optionally fall back to stamp or resolve branches

Prevention

When it happens

Trigger: Calling command.downgrade (or ScriptDirectory.run_downgrade) with an upper/current set that does not contain the downgrade target, or invoking _collect_downgrade_revisions directly with mismatched upper/lower identifiers. Also triggered when a branch_label filter leaves zero roots while a specific target was requested.

Common situations: Manually editing alembic_version in the DB to a revision not in the current branch; pointing downgrade at a revision id that only exists in a sibling branch; stale migration history after a rebase of migration files; passing a relative specifier like '-1' or 'branch@head' that resolves to nothing on the active path.

Related errors


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