A status removed from an enum, and eleven thousand rows still holding its value.
enum OrderStatus: string
{
case Pending = 'pending';
case Paid = 'paid';
// case AwaitingStock = 'awaiting_stock'; ← removed
}
// and every hydration of a 2019 order:
// ValueError: "awaiting_stock" is not a valid backing
// value for enum OrderStatus
// the fix, in order:
// 1. a data migration mapping the old value
// 2. THEN the enum change
// 3. and a CHECK constraint so it cannot recur
Removing an enum case is a schema change disguised as a code change, and the failure appears when a historical row is read rather than at deploy. Doing the data migration first is obvious in retrospect and the ordering had not been written down anywhere — it is now the same expand-and-contract rule that applies to columns.