sqlalchemy/alembic · error · RevisionError

Not a valid downgrade target from current heads

Error message

Not a valid downgrade target from current heads

What it means

Raised by _collect_downgrade_revisions when a branch-scoped downgrade leaves zero root revisions after filtering candidates by the branch's ancestors. Effectively, the requested downgrade target is not reachable / not valid from the current heads on that branch. It is a RevisionError.

Source

Thrown at alembic/script/revision.py:1380

            ancestors = {
                rev.revision
                for rev in self._get_ancestor_nodes(
                    [self._resolve_branch(branch_label)],
                    include_dependencies=False,
                )
            }
            # Intersection gives the root revisions we are trying to
            # rollback with the downgrade.
            roots = [
                is_revision(rev)
                for rev in self.get_revisions(
                    {rev.revision for rev in roots}.intersection(ancestors)
                )
            ]

            # Ensure we didn't throw everything away when filtering branches.
            if len(roots) == 0:
                raise RevisionError(
                    "Not a valid downgrade target from current heads"
                )

        heads = self.get_revisions(upper)

        # Aim is to drop :branch_revision; to do so we also need to drop its
        # descendents and anything dependent on it.
        downgrade_revisions = set(
            self._get_descendant_nodes(
                roots,
                include_dependencies=True,
                omit_immediate_dependencies=False,
            )
        )
        active_revisions = set(
            self._get_ancestor_nodes(heads, include_dependencies=True)
        )

View on GitHub (pinned to 44fb345033)

Solutions

  1. Drop the branch scope and downgrade by absolute revision id.
  2. Confirm the current heads and the target's lineage with 'alembic history' before downgrading.
  3. Ensure the branch you name actually contains a current head or a reachable ancestor of one.

Example fix

# before
alembic downgrade mybranch@deadbeef   # not valid from current heads
# after
alembic downgrade deadbeef             # absolute, unscoped
Defensive patterns

Strategy: validation

Validate before calling

heads = script.get_heads()
branch_rev = script.get_revision('%s@head' % branch_label)
if not script._revision_map._shares_lineage(branch_rev, heads):
    raise ValueError(
        'branch %s has no current head to downgrade from' % branch_label
    )

Try / catch

from alembic.script.revision import RevisionError
try:
    command.downgrade(cfg, '%s@%s' % (branch_label, target))
except RevisionError as e:
    if 'Not a valid downgrade target' in str(e):
        command.downgrade(cfg, target)  # retry unscoped

Prevention

When it happens

Trigger: Issuing a branch-scoped downgrade (e.g., 'alembic downgrade mybranch@<revid>') where the target is not on the current head lineage of mybranch, so after filtering roots the candidate set is empty.

Common situations: Attempting to downgrade a branch whose head has already been merged into / superseded by another branch; branch_label mismatch; targeting a revision that is not an ancestor of any current head on that branch.

Related errors


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