As already pointed out, many languages don't convert a dereference of a null pointer into a catchable exception. Doing that is a relatively modern trick. When the null pointer issue was first recognised, exceptions hadn't even been invented yet.
If you allow null pointers as a valid case, that is a special case. You need special-case handling logic, often in lots of different places. That's extra complexity.
IfWhether it relates to potentially null pointers or not, if you don't use exception throws to handle exceptional cases, you must handle those exceptional cases some other way. Typically, every function call must be have checks for those exceptional cases, either to prevent the function call being called inappropriately, or to detect the failure case when the function exits. That's extra complexity that can be avoided using exceptions.
More complexity usually means more errors.
Alternatives to using null pointers in data structures (e.g. to mark the start/end of a linked list) include using sentinel items. These can give the same functionality with a lot less complexity. However, there can be other ways to manage the complexity. One way is to wrap the potentially null pointer in a smart pointer class so that the null checks are only needed in one place.
What to do about the null pointer when it's detected? If you can't build in the exceptional-case handling, you could always throw an exception, and effectively delegate that special-case handling to the caller. And that's exactly what some languages do by default when you dereference a null pointer.