sqlalchemy/alembic · error · DependencyLoopDetected
Dependency self-loop is detected in revisions
Error message
Dependency self-loop is detected in revisions (%s)
What it means
Raised as DependencyLoopDetected when a Revision's dependencies list contains its own revision id (revision.py:1593-1596). While down_revision defines the parent chain, dependencies declare cross-branch ordering constraints — a revision cannot depend on itself, as that creates a circular dependency that cannot be topologically sorted.
Solutions
- Inspect the migration script's dependencies= argument and remove the revision's own id from it.
- Dependencies should only reference OTHER revisions that must be applied before this one.
- If you need to express that this revision depends on its own down_revision chain, that's already implicit — no dependency entry needed.
- Run 'alembic history --verbose' after fixing to confirm no cycles remain.
Example fix
# before revision = 'abc123' down_revision = 'parent789' dependencies = ['abc123', 'other456'] # self-dependency! # after revision = 'abc123' down_revision = 'parent789' dependencies = ['other456']
Defensive patterns
Strategy: validation
Validate before calling
def has_no_dependency_self_loop(revision, dependencies):
from alembic.util import to_tuple
if dependencies is None:
return True
return revision not in to_tuple(dependencies) Try / catch
from alembic.script.revision import DependencyLoopDetected
try:
script_dir.revision_map.add_revision(my_revision)
except DependencyLoopDetected as e:
print(f"Dependency self-loop: {e.revisions}")
raise Prevention
- Dependencies should reference OTHER revisions only, never the current revision.
- Review the dependencies= argument when writing cross-branch migrations.
- Run 'alembic history --verbose' after adding dependencies to catch issues early.
When it happens
Trigger: Constructing a Revision where revision appears in the dependencies tuple. Occurs at revision.py:1593: 'elif dependencies is not None and revision in util.to_tuple(dependencies)'. Triggered by adding a revision's own id to its dependencies= argument.
Common situations: Adding dependencies to express cross-branch ordering and accidentally including the current revision id. Copy-pasting a dependency list that already contained the revision. Merge revisions where the revision id was also listed as a dependency for clarity but creates a loop.
Related errors
- Self-loop is detected in revisions
- Character(s) ' ' not allowed in revision identifier
- Can't change down_revision on a refresh operation.
- Invalid plugin expression
- Not a valid downgrade target from current heads
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/a9c6c86f33477fbe.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/script/revision.py:1596
if illegal_chars:
raise RevisionError(
"Character(s) '%s' not allowed in revision identifier '%s'"
% (", ".join(sorted(illegal_chars)), revision)
)
def __init__(
self,
revision: str,
down_revision: str | tuple[str, ...] | None,
dependencies: str | tuple[str, ...] | None = None,
branch_labels: str | tuple[str, ...] | None = None,
) -> None:
if down_revision and revision in util.to_tuple(down_revision):
raise LoopDetected(revision)
elif dependencies is not None and revision in util.to_tuple(
dependencies
):
raise DependencyLoopDetected(revision)
self.verify_rev_id(revision)
self.revision = revision
self.down_revision = tuple_rev_as_scalar(util.to_tuple(down_revision))
self.dependencies = tuple_rev_as_scalar(util.to_tuple(dependencies))
self._orig_branch_labels = util.to_tuple(branch_labels, default=())
self.branch_labels = set(self._orig_branch_labels)
def __repr__(self) -> str:
args = [repr(self.revision), repr(self.down_revision)]
if self.dependencies:
args.append("dependencies=%r" % (self.dependencies,))
if self.branch_labels:
args.append("branch_labels=%r" % (self.branch_labels,))
return "%s(%s)" % (self.__class__.__name__, ", ".join(args))
def add_nextrev(self, revision: Revision) -> None:
self._all_nextrev = self._all_nextrev.union([revision.revision])View on GitHub (pinned to 5551b5d35f)