### 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
if not isinstance(difflist, list):
            difflist = [difflist]
        ret = []
        for diff in difflist:
            if 'dst_binary' in diff:
                ret.append(u"diff skipped: destination file appears to be binary\n")
            if 'src_binary' in diff:
                ret.append(u"diff skipped: source file appears to be binary\n")
            if 'dst_larger' in diff:
                ret.append(u"diff skipped: destination file size is greater than %d\n" % diff['dst_larger'])
            if 'src_larger' in diff:
                ret.append(u"diff skipped: source file size is greater than %d\n" % diff['src_larger'])
            if 'before' in diff and 'after' in diff:
                
                for x in ['before', 'after']:
                    if isinstance(diff[x], MutableMapping):
                        diff[x] = self._serialize_diff(diff[x])
                    elif diff[x] is None:
                        diff[x] = ''
                if 'before_header' in diff:
                    before_header = u"before: %s" % diff['before_header']
                else:
                    before_header = u'before'
                if 'after_header' in diff:
                    after_header = u"after: %s" % diff['after_header']
                else:
                    after_header = u'after'