{"record":{"id":"63469407ef96bdb4","repo":"sqlalchemy/alembic","slug":"revision-s-is-not-an-ancestor-of-revision-s","errorCode":null,"errorMessage":"Revision %s is not an ancestor of revision %s","messagePattern":"Revision (.+?) is not an ancestor of revision (.+?)","errorType":"exception","errorClass":"RangeNotAncestorError","httpStatus":null,"severity":"error","filePath":"alembic/script/revision.py","lineNumber":1415,"sourceCode":"        )\n\n        # Emit revisions to drop in reverse topological sorted order.\n        downgrade_revisions.intersection_update(active_revisions)\n\n        if implicit_base:\n            # Wind other branches back to base.\n            downgrade_revisions.update(\n                active_revisions.difference(self._get_ancestor_nodes(roots))\n            )\n\n        if (\n            target_revision is not None\n            and not downgrade_revisions\n            and target_revision not in heads\n        ):\n            # Empty intersection: target revs are not present.\n\n            raise RangeNotAncestorError(\"Nothing to drop\", upper)\n\n        return downgrade_revisions, heads\n\n    def _collect_upgrade_revisions(\n        self,\n        upper: _RevisionIdentifierType,\n        lower: _RevisionIdentifierType,\n        inclusive: bool,\n        implicit_base: bool,\n        assert_relative_length: bool,\n    ) -> tuple[set[Revision], tuple[Revision, ...]]:\n        \"\"\"\n        Compute the set of required revisions specified by :upper, and the\n        current set of active revisions specified by :lower. Find the\n        difference between the two to compute the required upgrades.\n\n        :inclusive=True includes the current/lower revisions in the set\n","sourceCodeStart":1397,"sourceCodeEnd":1433,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/5551b5d35f985c99cb8f1af2b3c526b050e4c059/alembic/script/revision.py#L1397-L1433","documentation":"Raised as RangeNotAncestorError during _collect_downgrade_revisions when the computed downgrade_revisions set is empty and the target revision is not among the current heads. This means the revision you are trying to downgrade away from is not actually present in the active migration path, so there is literally nothing to drop. The message 'Revision %s is not an ancestor of revision %s' comes from the RangeNotAncestorError class constructor.","triggerScenarios":"Calling alembic downgrade with a target revision that is not in the current heads' ancestry (revision.py:1408-1415: target_revision is not None, downgrade_revisions is empty, and target_revision not in heads). Common when passing a revision id from a different branch or a revision that was already removed.","commonSituations":"Downgrading toward a revision that lives on a branch not currently applied. Specifying a revision id from an old migration file that was deleted. Database is already at or below the target revision. Mixing revision identifiers from different projects/environments.","solutions":["Run 'alembic current' to see what revision the database is actually at.","Run 'alembic history' to confirm the target revision exists in the active lineage.","If the target is on a different branch, switch to the correct branch or merge branches first.","If already at/below the target, no downgrade is needed — the database state is already correct."],"exampleFix":"# before\nalembic downgrade abc123  # abc123 not in current lineage\n\n# after\nalembic current             # check actual DB state\nalembic history --verbose    # confirm lineage\nalembic downgrade <correct_ancestor_rev>","handlingStrategy":"validation","validationCode":"def is_valid_downgrade_target(script_dir, target_revision):\n    rev_map = script_dir.revision_map\n    target = rev_map.get_revision(target_revision)\n    if target is None:\n        return False\n    heads = rev_map.get_revisions(rev_map.heads)\n    # check that target is an ancestor of at least one current head\n    for head in heads:\n        ancestors = {\n            r.revision for r in rev_map._get_ancestor_nodes([head])\n        }\n        if target.revision in ancestors or target.revision == head.revision:\n            return True\n    return False","typeGuard":null,"tryCatchPattern":"from alembic.script.revision import RangeNotAncestorError\n\ntry:\n    command.downgrade(config, target)\nexcept RangeNotAncestorError as e:\n    print(f\"Target {e.upper} not in current lineage. Run 'alembic history' to check.\")\n    raise","preventionTips":["Run 'alembic current' before any downgrade to verify the DB state.","Keep a reference of valid revision ids for each environment.","Avoid downgrading across branches — merge first."],"tags":["alembic","migrations","downgrade","revision-map","ancestor"],"backgroundTag":null,"analyzedSha":"5551b5d35f985c99cb8f1af2b3c526b050e4c059","analyzedAt":"2026-08-11T01:38:46.612Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}