OK but that's an issue with using char* where does the union issue come in? I could understand an issue with a union between char* and, say, int or some other pointer, but I don't see what specific issue would arise from a union between the exact same pointer types.
The difference is that the code may try to execute x = "Hello", and then call free(y) (in case the condition is messed up). So the code will try to free() the pointer to a string literal (because they share the same memory location in the union).
The point I was making is that char*'s (and other pointer types as well) in unions make it harder to track ownership and lifetime by introducing implicit dependencies between data and increasing complexity with more code paths.
It's much easier to reason about the correctness of the code, when the fields of the data structures are mutated linearly and independently. This is especially important when the code is maintained by a number of people over long time.
Unfortunately, the C language does not have the capability to automatically check the correctness of memory management and object lifetimes, so the developer has to do their best to ensure the correctness of the code. In doing so, some coding practices can be better than others.
I am talking about generally good and bad coding practices here, not about the formal correctness of that particular piece of code. If some code works, then obviously it is correct, even if the code is obfuscated, non-human-readable or maintainable.