While the functions are distinct the call sites are self-documenting. You know which calls are for which purpose because the names are different. After combining them to deduplicate the code, you've lost that information, and to disentangle the abstraction now requires you to infer and reintroduce that lost information.
It's not that it can't be done, but there is real friction that doesn't just exist in people's heads.
I wrote a short post in roughly this idea space last year: https://t-ravis.com/post/doc/what_functions_and_why_function...
It feels like the same thread you're describing, but I guess it's pulling on the other end of it. It's thinking about how to name things in a way that makes it easier to see that the implementations might diverge later, and simplify actually doing so (by preserving more of this intentional context).
In contrast, duplicated code may be annoying, but it is usually simple to understand and maintain by anyone with knowledge of the project and the programming language.
Leaky abstractions are way way way worse than no abstraction at all.
This is bullshit. I think what you mean is that even a good abstraction can be used inappropriately.
You shouldn’t compound that with the fact that sometimes people create abstractions that are just bad.
The answer isn't to not abstract, the answer is to tear it out when it turns wrong. That was actually the original point of the popular article that streamlined this view - that we shouldn't be afraid of tearing them out, not that we shouldn't make them in the first place. Most people just read headlines though.