sqlalchemy/alembic · error · RevisionError
Character(s) '%s' not allowed in revision identifier '%s'
Error message
Character(s) '%s' not allowed in revision identifier '%s'
What it means
Raised by Revision.verify_rev_id (revision.py:1579) when the revision identifier string contains any character from _revision_illegal_chars, which is ['@', '-', '+', ':']. These characters are reserved: '@' selects branch labels, '+'/'-' express relative offsets, and ':' is used in resource/branch syntax. A revision id containing them would be ambiguous to the parser, so it is rejected at construction time.
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 44fb345033)
Solutions
- Replace illegal characters in the revision id: use underscores or nothing (e.g. '20240115' instead of '2024-01-15').
- If you already created migration files with bad ids, rename the revision AND every down_revision that references it across all files.
- Keep revision ids as pure hex (the default from alembic.revision) or alnum+underscore tokens.
- Run `alembic history` after fixing to confirm the graph still resolves.
Example fix
# before revision = '2024-01-15-init' down_revision = '2024-01-10' # after revision = '20240115_init' down_revision = '20240110'
Defensive patterns
Strategy: validation
Validate before calling
from alembic.script.revision import _revision_illegal_chars
def validate_rev_id(rev_id: str) -> None:
bad = set(rev_id).intersection(_revision_illegal_chars)
if bad:
raise ValueError(f'Illegal chars {sorted(bad)} in revision id {rev_id!r}; use [A-Za-z0-9_]')
validate_rev_id(my_new_revision_id) Type guard
import re
def is_valid_rev_id(rev_id: str) -> bool:
return isinstance(rev_id, str) and bool(re.fullmatch(r'[A-Za-z0-9_]+', rev_id)) Try / catch
from alembic.script.revision import RevisionError
try:
Revision.verify_rev_id(proposed_id)
except RevisionError as e:
proposed_id = re.sub(r'[@\-+:]', '_', proposed_id)
Revision.verify_rev_id(proposed_id) Prevention
- Use the default alembic-generated hex ids or alnum+underscore tokens.
- Avoid date formats with hyphens; use YYYYMMDD or underscores.
- Add a pre-commit hook that validates revision ids in new migration files.
When it happens
Trigger: Setting revision = 'rev-001' or '2024-01-15' or 'feat@2' in a migration file's revision = '...' line; programmatically building a Revision with an id that includes a hyphen (very common with date-formatted ids), plus sign, at-sign, or colon.
Common situations: Teams adopting ISO-date revision ids like '20240115_1200' is fine, but '2024-01-15' trips the hyphen check; copy-pasting a git branch name containing '/' is fine but one with '@' is not; autogenerated ids from external tooling that embeds version numbers with dashes.
Related errors
- Self-loop is detected in revisions (%s)
- Dependency self-loop is detected in revisions (%s)
- 'type' can be one of %s
- Invalid plugin expression {name!r}
- Revision %s is not an ancestor of revision %s
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/3f7bdc2a4eb9a9a6.json.
Report an issue: GitHub.