sqlalchemy/alembic · error · ValueError

This MigrationScript instance has a multiple-entry list for…

Error message

This MigrationScript instance has a multiple-entry list for DowngradeOps; please use the downgrade_ops_list attribute.

What it means

Raised as ValueError by the MigrationScript.downgrade_ops getter (ops.py:2847-2859) when self._downgrade_ops has more than one entry. The exact mirror of the upgrade_ops (errorIndex 28) check: in multidb autogenerate, multiple DowngradeOps are stored and the singular accessor is intentionally disabled, pointing to downgrade_ops_list.

Solutions

  1. Use migration_script.downgrade_ops_list and iterate it.
  2. In custom hooks, write `for do in script.downgrade_ops_list: ...` instead of `script.downgrade_ops`.
  3. Confirm your env.py matches the single-DB pattern if you truly expect one DowngradeOps.

Example fix

// before
script = directives[0]
down = script.downgrade_ops  # raises in multidb
// after
script = directives[0]
for down in script.downgrade_ops_list:
    ...  # handle each DB's downgrade
Defensive patterns

Strategy: validation

Validate before calling

# In process_revision_directives, always iterate the downgrade list.
def process_revision_directives(context, revision, directives):
    script = directives[0]
    for down_ops in script.downgrade_ops_list:  # never script.downgrade_ops
        ...

Type guard

from alembic.operations.ops import MigrationScript

def is_multidb(script: MigrationScript) -> bool:
    return len(script._downgrade_ops) > 1

Try / catch

try:
    down = script.downgrade_ops
except ValueError:
    down = None
ops_list = script.downgrade_ops_list  # always safe

Prevention

When it happens

Trigger: Accessing migration_script.downgrade_ops on a multi-database MigrationScript where >1 DowngradeOps were generated. Typically inside a process_revision_directives hook or custom renderer that assumes a single downgrade op.

Common situations: Multidb template migrations; refactoring a single-DB hook to run against a multi-DB project; assuming the script has one downgrade when env.py iterates engines.

Related errors


AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11). Data as JSON: /api/errors/86830460d40dc14f. Report an issue: GitHub.

Appendix: source

Thrown at alembic/operations/ops.py:2856

        else:
            return self._upgrade_ops[0]

    @upgrade_ops.setter
    def upgrade_ops(self, upgrade_ops: UpgradeOps | list[UpgradeOps]) -> None:
        self._upgrade_ops = util.to_list(upgrade_ops)
        for elem in self._upgrade_ops:
            assert isinstance(elem, UpgradeOps)

    @property
    def downgrade_ops(self) -> DowngradeOps | None:
        """An instance of :class:`.DowngradeOps`.

        .. seealso::

            :attr:`.MigrationScript.downgrade_ops_list`
        """
        if len(self._downgrade_ops) > 1:
            raise ValueError(
                "This MigrationScript instance has a multiple-entry "
                "list for DowngradeOps; please use the "
                "downgrade_ops_list attribute."
            )
        elif not self._downgrade_ops:
            return None
        else:
            return self._downgrade_ops[0]

    @downgrade_ops.setter
    def downgrade_ops(
        self, downgrade_ops: DowngradeOps | list[DowngradeOps]
    ) -> None:
        self._downgrade_ops = util.to_list(downgrade_ops)
        for elem in self._downgrade_ops:
            assert isinstance(elem, DowngradeOps)

    @property

View on GitHub (pinned to 5551b5d35f)