sqlalchemy/alembic · error · DependencyLoopDetected
Dependency self-loop is detected in revisions (%s)
Error message
Dependency self-loop is detected in revisions (%s)
What it means
Raised as DependencyLoopDetected (revision.py:1596, kind='Dependency self-loop', subclass of both DependencyCycleDetected and LoopDetected) during Revision.__init__ when the revision's own id appears inside its dependencies tuple. Like a down_revision self-loop, a dependency on itself is a degenerate cycle and is rejected immediately.
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 44fb345033)
Solutions
- Remove the self-referential id from the dependencies tuple/list.
- Set dependencies to other branch heads you genuinely depend on, or omit the parameter entirely.
- Re-run `alembic history --verbose` to confirm dependency edges are between distinct revisions.
Example fix
# before
revision = 'abc123'
dependencies = ('abc123', 'def456')
# after
revision = 'abc123'
dependencies = ('def456',) Defensive patterns
Strategy: validation
Validate before calling
def check_no_dependency_self_loop(revision: str, dependencies):
deps = (dependencies,) if isinstance(dependencies, str) else (dependencies or ())
if revision in deps:
raise ValueError(f'revision {revision!r} cannot depend on itself')
check_no_dependency_self_loop(revision, dependencies) Try / catch
from alembic.script.revision import DependencyLoopDetected
try:
rev = Revision(revision, down_revision, dependencies=dependencies)
except DependencyLoopDetected as e:
print(f'Dependency self-loop: {e}. Remove self from dependencies.') Prevention
- When listing cross-branch dependencies, double-check the list excludes the current revision id.
- Generate merge revisions via `alembic merge` rather than editing dependencies by hand.
- Run `alembic history --verbose` to sanity-check dependency edges.
When it happens
Trigger: Writing a migration with revision = 'abc' and dependencies = ('abc',) or dependencies = 'abc'; programmatically constructing Revision with a dependency list that includes the revision's own id.
Common situations: Adding a cross-branch dependency and accidentally listing the current revision; merge-generated dependency lists that copy the revision id.
Related errors
- Self-loop is detected in revisions (%s)
- Revision %s is not an ancestor of revision %s
- Character(s) '%s' not allowed in revision identifier '%s'
- 'type' can be one of %s
- SQLAlchemy 2.0 required
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/a9c6c86f33477fbe.json.
Report an issue: GitHub.