Complex case—wrapper APIs. At times, one can be forgiven for thinking that all programmers are taught: after calling malloc (or calloc, realloc), call free. malloc and free go together! How hard can that be? Why are there are so many sneaky leakage bugs if this is the case?
A key reason that leakage defects occur and are hard to pinpoint is because some APIs—often, third-party library APIs—might internally perform dynamic memory allocation and expect the caller to free the memory. The API will (hopefully) document this important fact; but who (tongue in cheek) reads documentation?
That's really the crux of the issue in real-world software; it is complex and we work on large, complex projects. It is indeed easy to miss the fact that an underlying API allocates memory and the caller is responsible for freeing it. Precisely this occurs quite often.
There's another case: on complex codebases (especially those with spaghetti code), where a lot of deeply nested layers entangle the code, it can get especially hard to perform the required cleanup—including memory-frees—on every possible error case.