The key problem has been that the resulting programs are fantastically hard to understand. One of the things I think Dijkstra got right in "Goto Considered Harmful" is that humans can reason about programs better when there's a strong correspondence between the textual structure of the code and its execution structure at runtime.
Runtime metaprogramming deliberately upends that.
I think it's probably totally fine to use those kinds of features as debugging aids when hacking on a program and figuring out where stuff is going wrong. That aligns with many of the use cases Hillel lists here. But you probably don't want to commit code like that to the repo and require everyone else maintaining the program to understand it.
For better or worse, we seem to lean strongly towards languages and language features that are used not just during the development process but also make sense in committed, maintained code.
Dynamically typed languages lean in the other direction. They can be very powerful and productive while you're in the middle of working on a program, but the resulting artifact is harder for others to understand and maintain.
Dynamically typed languages to me feel like unbaked plasticine. You can have a lot of fun molding it and you can get it into the shape you want much quicker than you could carving wood or stone. But if a dozen people are collaborating on a sculpture over months or years, it's impossible to build on top of someone else's plasticine part without it shushing down and falling apart.
Or course, the holy grail is baked plasticine: a language that is dynamically typed and moldable while you're figuring stuff out and then can be baked with static types for the parts that are solid and need to be reliable. I've yet to see an optionally or gradually typed language pull that off well. The boundary between them just gets too weird.