{"id":"13ef770942a4ff2e","repo":"sqlalchemy/alembic","slug":"self-loop-is-detected-in-revisions-s","errorCode":null,"errorMessage":"Self-loop is detected in revisions (%s)","messagePattern":"Self-loop is detected in revisions \\((.+?)\\)","errorType":"exception","errorClass":"LoopDetected","httpStatus":null,"severity":"error","filePath":"alembic/script/revision.py","lineNumber":1592,"sourceCode":"\n    @classmethod\n    def verify_rev_id(cls, revision: str) -> None:\n        illegal_chars = set(revision).intersection(_revision_illegal_chars)\n        if illegal_chars:\n            raise RevisionError(\n                \"Character(s) '%s' not allowed in revision identifier '%s'\"\n                % (\", \".join(sorted(illegal_chars)), revision)\n            )\n\n    def __init__(\n        self,\n        revision: str,\n        down_revision: str | tuple[str, ...] | None,\n        dependencies: str | tuple[str, ...] | None = None,\n        branch_labels: str | tuple[str, ...] | None = None,\n    ) -> None:\n        if down_revision and revision in util.to_tuple(down_revision):\n            raise LoopDetected(revision)\n        elif dependencies is not None and revision in util.to_tuple(\n            dependencies\n        ):\n            raise DependencyLoopDetected(revision)\n\n        self.verify_rev_id(revision)\n        self.revision = revision\n        self.down_revision = tuple_rev_as_scalar(util.to_tuple(down_revision))\n        self.dependencies = tuple_rev_as_scalar(util.to_tuple(dependencies))\n        self._orig_branch_labels = util.to_tuple(branch_labels, default=())\n        self.branch_labels = set(self._orig_branch_labels)\n\n    def __repr__(self) -> str:\n        args = [repr(self.revision), repr(self.down_revision)]\n        if self.dependencies:\n            args.append(\"dependencies=%r\" % (self.dependencies,))\n        if self.branch_labels:\n            args.append(\"branch_labels=%r\" % (self.branch_labels,))","sourceCodeStart":1574,"sourceCodeEnd":1610,"githubUrl":"https://github.com/sqlalchemy/alembic/blob/44fb3450330204b222ff05135e1fbbbdb28c44db/alembic/script/revision.py#L1574-L1610","documentation":"Raised as LoopDetected (revision.py:1592, kind='Self-loop') during Revision.__init__ when the revision's own id appears inside its down_revision (after tuple normalization). A migration cannot be its own parent — that would create a cycle of length one in the revision DAG, making upgrade/downgrade traversal non-terminating.","triggerScenarios":"Writing a migration file where revision = 'abc' and down_revision = 'abc'; building a Revision('xyz', down_revision='xyz') programmatically; a copy-paste error where the down_revision string duplicates the revision string.","commonSituations":"Duplicating a migration template and forgetting to change down_revision; automated tooling that sets down_revision to the last known id which happens to equal the new id after a naming collision.","solutions":["Open the offending migration file and set down_revision to the actual previous revision id (or None for a base).","If this is a new root migration, use down_revision = None.","Validate with `alembic history` to confirm the chain is linear where expected."],"exampleFix":"# before\nrevision = 'abc123'\ndown_revision = 'abc123'\n# after\nrevision = 'abc123'\ndown_revision = 'parent456'","handlingStrategy":"validation","validationCode":"def check_no_self_loop(revision: str, down_revision):\n    downs = (down_revision,) if isinstance(down_revision, str) else (down_revision or ())\n    if revision in downs:\n        raise ValueError(f'revision {revision!r} cannot be its own down_revision')\n\ncheck_no_self_loop(revision, down_revision)","typeGuard":null,"tryCatchPattern":"from alembic.script.revision import LoopDetected\ntry:\n    rev = Revision(revision, down_revision)\nexcept LoopDetected as e:\n    print(f'Self-loop: {e}. Set down_revision to the real parent.')","preventionTips":["When templating a new migration, never copy down_revision from the revision field.","Use `alembic revision` (autogeneration) rather than hand-writing ids.","Review the revision/down_revision pair before saving any migration file."],"tags":["alembic","migration","revision-graph","cycle","validation"],"analyzedSha":"44fb3450330204b222ff05135e1fbbbdb28c44db","analyzedAt":"2026-08-04T19:57:10.248Z","schemaVersion":2}