Semver is nearly impossible to do "properly" because of https://xkcd.com/1172. With a sufficient number of users, all bug fixes are breaking changes. If the behavior can possibly be observed in any way, some user will be depending on it, deliberately or otherwise.
Semver defines what is breaking and not-breaking. E.g., Rust semver says that "code should continue compiling with a minor version bump, but not necessarily for a major version bump"
> Software using Semantic Versioning MUST declare a public API. This API could be declared in the code itself or exist strictly in documentation. However it is done, it SHOULD be precise and comprehensive.
If it's not in the API, it is not bound by the rules. Many ecosystems come up with various norms, like Rust has, to help guide people in this. But it's almost certainly not a semver violation to make the change described in the XKCD because "handle unknown unknowns" is not possible. That doesn't mean that we should throw out the entire idea of software assisted upgrades to dependencies.
Semver doesn't stop people from depending on unstable/implementation-specific behaviour; it needs to be coupled with a strong mechanism for defining what behaviour is defined by an API, and the result is that "the bug" is with all those users who depend on un-guaranteed behaviour.
The breaks happen regardless, but you have a principled way of defining whose fault/problem it is.
I would argue that https://xkcd.com/1172 is a case where the user "deserves" the breaking change, because they relied on a hack in the first place.
That's the thing: I feel like people tend to call "dependency hell" what I would consider downright malpractice. "Shared libraries don't work because they require good practice" is, IMO, not a good argument against shared libraries. If you need to design your tool with the constraints that "users will use it wrongly", then it's already lost.