sqlalchemy/alembic · error · ValueError
This MigrationScript instance has a multiple-entry list for…
Error message
This MigrationScript instance has a multiple-entry list for UpgradeOps; please use the upgrade_ops_list attribute.
What it means
Raised as ValueError by the MigrationScript.upgrade_ops getter (ops.py:2822-2835) when self._upgrade_ops contains more than one entry. A MigrationScript normally holds a single UpgradeOps; but in a multi-database ('multidb') autogenerate setup multiple UpgradeOps (one per database) are stored, and the singular accessor is disabled on purpose, directing callers to the upgrade_ops_list attribute.
Solutions
- Use migration_script.upgrade_ops_list, which always returns a list (single- or multi-entry), and iterate it.
- In custom hooks, pick the right entry: `for uo in script.upgrade_ops_list: ...` rather than `script.upgrade_ops`.
- If you truly expect a single DB, ensure your env.py does not register multiple UpgradeOps.
Example fix
// before
def process_revision_directives(context, revision, directives):
script = directives[0]
up = script.upgrade_ops # raises in multidb
// after
def process_revision_directives(context, revision, directives):
script = directives[0]
for up in script.upgrade_ops_list:
up.ops.append(my_custom_op) Defensive patterns
Strategy: validation
Validate before calling
# In process_revision_directives, always iterate the list form.
def process_revision_directives(context, revision, directives):
script = directives[0]
for up_ops in script.upgrade_ops_list: # never script.upgrade_ops
up_ops.ops.extend(my_extra_ops) Type guard
from alembic.operations.ops import MigrationScript
def is_multidb(script: MigrationScript) -> bool:
return len(script._upgrade_ops) > 1 Try / catch
try:
up = script.upgrade_ops
except ValueError:
up = None
ops_list = script.upgrade_ops_list # always safe Prevention
- Prefer script.upgrade_ops_list over script.upgrade_ops in any generic/custom code.
- Detect multidb by len(script._upgrade_ops) > 1 before touching the singular accessor.
- Write process_revision_directives hooks to iterate the list, not assume one entry.
When it happens
Trigger: Accessing migration_script.upgrade_ops on a script produced by a multi-database (multidb) env.py / template where configure() was called per-DB, yielding a list of >1 UpgradeOps. Common in `alembic revision --autogenerate` against multiple engines.
Common situations: Switching a project from single-DB to the multidb template, or writing custom autogenerate hooks (e.g. in a `process_revision_directives` function) that assume a single UpgradeOps but receive a list.
Related errors
- This MigrationScript instance has a multiple-entry list for…
- constraint cannot be produced; original constraint is not…
- no dispatch function for object
- operation is not reversible; original column is not present
- A plugin named is already registered
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/ce917bf9b0935879.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/operations/ops.py:2831
self.splice = splice
self.branch_label = branch_label
self.version_path = (
pathlib.Path(version_path).as_posix() if version_path else None
)
self.depends_on = depends_on
self.upgrade_ops = upgrade_ops
self.downgrade_ops = downgrade_ops
@property
def upgrade_ops(self) -> UpgradeOps | None:
"""An instance of :class:`.UpgradeOps`.
.. seealso::
:attr:`.MigrationScript.upgrade_ops_list`
"""
if len(self._upgrade_ops) > 1:
raise ValueError(
"This MigrationScript instance has a multiple-entry "
"list for UpgradeOps; please use the "
"upgrade_ops_list attribute."
)
elif not self._upgrade_ops:
return None
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`.View on GitHub (pinned to 5551b5d35f)