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

  1. Replace illegal characters in the revision id: use underscores or nothing (e.g. '20240115' instead of '2024-01-15').
  2. If you already created migration files with bad ids, rename the revision AND every down_revision that references it across all files.
  3. Keep revision ids as pure hex (the default from alembic.revision) or alnum+underscore tokens.
  4. 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

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


AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04). Data as JSON: /data/errors/3f7bdc2a4eb9a9a6.json. Report an issue: GitHub.