sqlalchemy/alembic · error · ValueError
Duplicate table keys across multiple MetaData objects: %s
Error message
Duplicate table keys across multiple MetaData objects: %s
What it means
Alembic aggregates multiple MetaData objects passed to configure() into one table-key map; the table_key_to_table property refuses to silently merge two MetaData objects that declare the same (schema, table_name) key, because doing so would be ambiguous. This guards against accidentally wiring the same model metadata in twice, or two metadata sets that overlap.
Source
Thrown at alembic/autogenerate/api.py:513
return result
@util.memoized_property
def table_key_to_table(self) -> dict[str, Table]:
"""Return an aggregate of the :attr:`.MetaData.tables` dictionaries.
The :attr:`.MetaData.tables` collection is a dictionary of table key
to :class:`.Table`; this method aggregates the dictionary across
multiple :class:`.MetaData` objects into one dictionary.
Duplicate table keys are **not** supported; if two :class:`.MetaData`
objects contain the same table key, an exception is raised.
"""
result: dict[str, Table] = {}
for m in util.to_list(self.metadata):
intersect = set(result).intersection(set(m.tables))
if intersect:
raise ValueError(
"Duplicate table keys across multiple "
"MetaData objects: %s"
% (", ".join('"%s"' % key for key in sorted(intersect)))
)
result.update(m.tables)
return result
class RevisionContext:
"""Maintains configuration and state that's specific to a revision
file generation operation."""
generated_revisions: list[MigrationScript]
process_revision_directives: ProcessRevisionDirectiveFn | None
def __init__(
self,View on GitHub (pinned to 44fb345033)
Solutions
- De-duplicate: ensure each table is declared in exactly one MetaData and pass the union metadata once.
- If duplicates are intentional (e.g. per-schema splits), rename one table or assign distinct schemas so the keys differ.
- Pass a single combined MetaData that already holds all tables instead of a list with overlap.
Example fix
// before
context.configure(target_metadata=[base.metadata, plugin.metadata])
# both define Table('users', ...) -> ValueError
// after
context.configure(target_metadata=base.metadata) # plugin tables also attached here Defensive patterns
Strategy: validation
Validate before calling
from sqlalchemy import MetaData
metas = [m for m in (target_metadata if isinstance(target_metadata, (list, tuple)) else [target_metadata])]
seen, dupes = set(), set()
for m in metas:
for k in m.tables:
(dupes if k in seen else seen).add(k)
assert not dupes, f"Duplicate table keys across MetaData objects: {sorted(dupes)}" Type guard
def has_unique_table_keys(metas) -> bool:
keys = set()
for m in metas:
inter = keys.intersection(m.tables)
if inter:
return False
keys.update(m.tables)
return True Prevention
- Pass a single declarative Base.metadata rather than a list.
- If splitting metadata, ensure each table lives in exactly one MetaData.
- Add a unit test that asserts table key uniqueness before configure().
When it happens
Trigger: Passing a list/tuple of MetaData objects to EnvironmentContext.configure(target_metadata=[meta_a, meta_b]) where meta_a and meta_b both define a Table with the same key (e.g. both define 'users', or 'myschema.users').
Common situations: App registers the same declarative Base.metadata in two separate MetaData groupings; a plugin or shared library contributes a table that the app also defines; a refactor split metadata but left duplicate declarations.
Related errors
- can't return inspector as this AutogenContext has no databas
- Autogenerate rendering of SQL Expression language constructs
- Can only return single object for UpgradeOps traverse
- Can only return single object for DowngradeOps traverse
- '%s' is not a valid value for %s; expected 'space', 'newline
AI-assisted analysis of sqlalchemy/alembic@44fb345033 (2026-08-04).
Data as JSON: /data/errors/b0d2938a8131b6a9.json.
Report an issue: GitHub.