Lost a lot of hours to this one.
In STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_i2s.c, at line 1150 we have the I2S_WaitFlagStateUntilTimeout function.
The description of this function (and its use in the HAL_I2S_Receive) indicate it should spin until the flag equals the expected passed State or until timeout.
In the first section of the function (State==RESET) we’re waiting for the flag to turn from SET to RESET. Unfortunately, the while loop that follows will count down and eventually timeout if the state is in RESET. If it’s SET (when it should be waiting and counting), it instead jumps to the bottom of the function and returns HAL_OK.
This has the unfortunate property of immediately returning HAL_OK if the flag is *not* in the state you’re looking for it to be, and counting down and eventually timing out if it *is* in the proper state.
Despite the hours I spent tracking this down, it’s an easy fix — simply switch the while loops from == to != and vice versa. Hope this saves someone a lot of the headaches it introduced for me.