You will want to change this into:
.order("COALESCE(CAST(custom_fields.value AS timestamp), custom_fields.value) DESC")
for readability.
You sort by sortDate and when that is missing you use createdAt. All in descending order. The query is returning the data exactly as asked as 2020 is “greater” than the 2011 second place.
If you want nulls last, you need:
.order("CAST(custom_fields.value AS timestamp DESC NULLS LAST")