Block messages with transactions hashes instead of full transactions
There are probably several reasons, but it's mostly historic. There is no inherent problem with just sending transaction id's. There is a disadvantage too, though, namely increased latency, which is not necessarily a problem when doing an initial sync, but it is not wanted when a fresh new block is propagating.

In fact, this idea is part of BIP 37, which specifies a protocol extension for requesting filtered blocks. To avoid the increased latency, matching transactions that are not known to the server to be already known to the receiver, are sent immediately after the filtered block itself.

Edit: BIP152 compact blocks also use this idea, though they use shortened 48-bit transaction ids instead to save space.