### Summary

If a module calls `fail_json()` and provides `exception=None`, this will crash every callback that uses `_handle_exception` from `CallbackBase`: if verbosity is < 3, it will try to call `split` on `None`, and otherwise it will try to concatenate a string with `None` (which results in `TypeError`).

### Issue Type

Bug Report

### Component Name

lib/ansible/plugins/callback/__init__.py

### Ansible Version

```console
devel
```


### Configuration

```console
*
```


### OS / Environment

*

### Steps to Reproduce

*

### Expected Results

*

### Actual Results

```console
*
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
"""Return label for the hostname (& delegated hostname) of a task
        result.
        """
        label = "%s" % result._host.get_name()
        if result._task.delegate_to and result._task.delegate_to != result._host.get_name():
            
            label += " -> %s" % result._task.delegate_to
            
            ahost = result._result.get('_ansible_delegated_vars', {}).get('ansible_host', result._task.delegate_to)
            if result._task.delegate_to != ahost:
                label += "(%s)" % ahost
        return label
    def _run_is_verbose(self, result, verbosity=0):
        return ((self._display.verbosity > verbosity or result._result.get('_ansible_verbose_always', False) is True)
                and result._result.get('_ansible_verbose_override', False) is False)
    def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False, serialize=True):
        try:
            result_format = self.get_option('result_format')
        except KeyError:
            
            result_format = 'json'
        try:
            pretty_results = self.get_option('pretty_results')
        except KeyError:
            
            pretty_results = None
        indent_conditions = (
            result.get('_ansible_verbose_always'),