Saish Naik

Jan 07, 2026 • 2 min read

🚨Alembic Migration Issue: create_foreign_key(None, …) — and Why Your Downgrades Break



I had run into an interesting (and slightly risky) Alembic behavior while evolving a SQLAlchemy schema, and I thought it was worth sharing for others working with migrations.
While adding a new optional foreign key column to an existing table, Alembic autogenerated the migration like this 👇

op.create_foreign_key(None, 'order', 'invoice', ['invoice_id'], ['id'])

The constraint name is None.
Looks harmless — but this causes a downgrade failure later.
Why?
Because:
1️⃣ The database still creates a real FK constraint name
2️⃣ Alembic has no record of that actual name
3️⃣ The autogenerated downgrade tries to drop None

At downgrade time Alembic can’t map:
“constraint named None”
to
“the actual FK constraint in the database”
This can fail — or worse — drop the wrong constraint if multiple FKs exist.
This usually happens when:
a new FK is added in a later migration (not during table creation)
the column is nullable / optional
and no naming convention is defined

Solution 1 — Add a Global Naming Convention (Recommended)
I updated the SQLAlchemy metadata to use a deterministic naming scheme for all constraints:

This makes Alembic generate stable names like:
fk_order_invoice_id_invoice

Now the migration becomes:


And downgrade drops the exact same name.
No surprises. No flaky downgrades. Migration history stays deterministic.

Solution 2 — Explicitly Name the FK in the Model (Case-by-Case Fix)
When I want tight control on a specific constraint, I can also name it directly:

This is especially useful when:
patching an existing migration
or when only a single FK needs stabilizing

🎯 Key Takeaways
Alembic may emit create_foreign_key(None, …) for newly added FKs
The DB generates a real constraint name — but Alembic doesn’t know it
Downgrades can fail or behave unpredictably
Always ensure FK names are deterministic
Either:
- define a global naming convention (best long-term approach)
or
- explicitly name the FK constraint in the model or migration

I’m sharing this because issues like this rarely show up until:
the schema gets larger
multiple FKs exist on one table
or a downgrade is actually needed
…and then it can become painful very fast 🙂

If you’ve run into similar Alembic quirks would love to hear your experience 👇

Join Saish on Peerlist!

Join amazing folks like Saish and thousands of other builders on Peerlist.

peerlist.io/

It’s available... this username is available! 😃

Claim your username before it's too late!

This username is already taken, you’re a little late.😐

0

1

0