By the time to_variant_object runs, that array has no VOID element left in it.

ARRAY(PAYMENT_DATE, TRANSACTION_DATE) resolves a common element type across its arguments, so the expression is already ARRAY<DATE>. The check walks array element types, map value types and struct field types against one accepted-type list that excludes the untyped NULL type, and a struct keeps each field's own declared type, so nothing promotes PAYMENT_DATE there.

  1. Confirm the coercion: TYPEOF(ARRAY(PAYMENT_DATE, TRANSACTION_DATE)) returns array<date>, while TYPEOF(ARRAY(PAYMENT_DATE)) returns array<void>.

  2. Confirm the container is not what decides it: TO_VARIANT_OBJECT(ARRAY(PAYMENT_DATE)) and TO_VARIANT_OBJECT(MAP('k', PAYMENT_DATE)) both fail with the same DATATYPE_MISMATCH.CAST_WITHOUT_SUGGESTION, and TO_VARIANT_OBJECT(ARRAY(INTERVAL '1' SECOND)) fails too, so VOID is not being singled out.

  3. Confirm what SCHEMA_OF_VARIANT reports: SCHEMA_OF_VARIANT(TO_VARIANT_OBJECT(ARRAY(CAST(NULL AS DATE)))) returns ARRAY<VOID> even though the input type was ARRAY<DATE>. VARIANT stores one untyped null for every null value, so your cast did apply and the field simply holds that null.

On whether it is deliberate, I can only speak to observed behavior: the exclusion applies uniformly to arrays, maps and structs, and I have not found anything public stating whether leaving the untyped NULL type out of the accepted list was a design decision.