I saw a LIBUSB_ERROR_IO on read only on Linux, but not Windows for the exact same code.
The root cause of my issues was that I was trying to do partial reads of bulk transfer messages. For example, if the whole message was 14 bytes I was trying to read 10 bytes then 4 bytes in separate libusb_bulk_transfer() calls.
Changing my code to a single 14 byte libusb_bulk_transfer() read now works properly on both platforms.