sqlalchemy/alembic · error · RevisionError
Character(s) ' ' not allowed in revision identifier
Error message
Character(s) '%s' not allowed in revision identifier '%s'
What it means
Raised by Revision.verify_rev_id when a revision identifier contains characters from the illegal set ['@', '-', '+', ':']. These characters are reserved by Alembic's revision syntax: '@' separates branch labels, '+'/'-' express relative offsets, and ':' separates revision ranges. Allowing them in identifiers would make revision references ambiguous.
Solutions
- Replace dashes in revision ids with underscores: use '550e8400_e29b_41d4_a716_446655440000' instead of dashes.
- Remove any '@', '+', ':' characters from the revision identifier in the migration script's revision = '...' line.
- If using a custom revision id generator (revision_environment=True or script.py.x), strip or replace illegal characters before assignment.
- Use Alembic's default rev_id generator which produces safe hex strings.
Example fix
# before revision = '550e8400-e29b-41d4-a716-446655440000' down_revision = '3f4d2a1b' # after revision = '550e8400e29b41d4a716446655440000' down_revision = '3f4d2a1b'
Defensive patterns
Strategy: validation
Validate before calling
_ILLEGAL = {'@', '-', '+', ':'}
def is_valid_rev_id(rev_id):
return bool(rev_id) and not set(rev_id).intersection(_ILLEGAL)
def sanitize_rev_id(rev_id):
for ch in _ILLEGAL:
rev_id = rev_id.replace(ch, '_')
return rev_id Type guard
def is_valid_revision_id(rev_id: str) -> bool:
"""Type guard: True if rev_id contains no illegal characters."""
illegal = {'@', '-', '+', ':'}
return isinstance(rev_id, str) and len(rev_id) > 0 and not set(rev_id) & illegal Prevention
- Use Alembic's default rev_id generator (hex strings) to avoid illegal characters entirely.
- If using custom rev_id callbacks, sanitize by replacing '@', '-', '+', ':' with underscores.
- Add a pre-commit hook that validates revision ids in migration files.
When it happens
Trigger: Creating or registering a Revision (or Script) whose 'revision' string contains '@', '-', '+', or ':'. Happens at revision.py:1577-1582 during Revision.__init__ via verify_rev_id. Common when a custom revision id generator or hand-edited migration script uses an illegal character.
Common situations: Using a UUID with dashes as revision id (e.g. '550e8400-e29b-41d4-a716-446655440000'). Setting revision = 'feat@v2' thinking '@' is a valid separator. Using '+1' or ':3' style strings. Custom revision id callbacks that don't sanitize characters.
Related errors
- Dependency self-loop is detected in revisions
- Self-loop is detected in revisions
- 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/3f7bdc2a4eb9a9a6.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/script/revision.py:1579
From a migration standpoint, these dependencies are added to the
down_revision to form the full iteration. However, the separation
of down_revision from "dependencies" is to assist in navigating
a history that contains many branches, typically a multi-root scenario.
"""
branch_labels: set[str] = None # type: ignore[assignment]
"""Optional string/tuple of symbolic names to apply to this
revision's branch"""
_resolved_dependencies: tuple[str, ...]
_normalized_resolved_dependencies: tuple[str, ...]
@classmethod
def verify_rev_id(cls, revision: str) -> None:
illegal_chars = set(revision).intersection(_revision_illegal_chars)
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)
View on GitHub (pinned to 5551b5d35f)