# Buggy code
def list_collection_dirs(search_paths=None, coll_filter=None):
    """
    Return paths for the specific collections found in passed or configured search paths
    :param search_paths: list of text-string paths, if none load default config
    :param coll_filter: limit collections to just the specific namespace or collection, if None all are returned
    :return: list of collection directory paths
    """

    collections = defaultdict(dict)
    for path in list_valid_collection_paths(search_paths):
        if os.path.isdir(path):
            coll_root = os.path.join(path, "ansible_collections")

            if os.path.exists(coll_root) and os.path.isdir(coll_root):
                coll = None
                if coll_filter is None:
                    namespaces = os.listdir(coll_root)
                else:
                    if "." in coll_filter:
                        (nsp, coll) = coll_filter.split(".")
                    else:
                        nsp = coll_filter
                    namespaces = [nsp]

                for ns in namespaces:
                    namespace_dir = os.path.join(coll_root, ns)

                    if os.path.isdir(namespace_dir):
                        if coll is None:
                            colls = os.listdir(namespace_dir)
                        else:
                            colls = [coll]

                        for collection in colls:
                            # skip dupe collections as they will be masked in execution
                            if collection not in collections[ns]:
                                coll_dir = os.path.join(namespace_dir, collection)
                                if is_collection_path(coll_dir):
                                    cpath = os.path.join(namespace_dir, collection)
                                    collections[ns][collection] = cpath
                                    yield cpath


# Traceback (TypeError)
ansible-doc 2.9.7.post0
config file = None
configured module search path = ['/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /ansible/lib/ansible
executable location = /ansible/bin/ansible-doc
python version = 3.8.2 (default, Apr  8 2020, 14:31:25) [GCC 9.3.0]
No config file found; using defaults
/ansible/lib/ansible/cli/doc.py:219: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
doc_data['return'] = yaml.load(doc_data['return'])
ERROR! Unexpected Exception, this is probably a bug: Object of type datetime is not JSON serializable
the full traceback was:

Traceback (most recent call last):
File "/ansible/bin/ansible-doc", line 123, in <module>
exit_code = cli.run()
File "/ansible/lib/ansible/cli/doc.py", line 223, in run
jdump(plugin_docs)
File "/ansible/lib/ansible/cli/doc.py", line 36, in jdump
display.display(json.dumps(text, sort_keys=True, indent=4))
File "/usr/lib/python3.8/json/__init__.py", line 234, in dumps
return cls(
File "/usr/lib/python3.8/json/encoder.py", line 201, in encode
chunks = list(chunks)
File "/usr/lib/python3.8/json/encoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
[Previous line repeated 1 more time]
File "/usr/lib/python3.8/json/encoder.py", line 325, in _iterencode_list
yield from chunks
File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/lib/python3.8/json/encoder.py", line 438, in _iterencode
o = _default(o)
File "/usr/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable

# Fixed code
def list_collection_dirs(search_paths=None, coll_filter=None):
    """
    Return paths for the specific collections found in passed or configured search paths
    :param search_paths: list of text-string paths, if none load default config
    :param coll_filter: limit collections to just the specific namespace or collection, if None all are returned
    :return: list of collection directory paths
    """

    collections = defaultdict(dict)
    for path in list_valid_collection_paths(search_paths):
        b_path = to_bytes(path)
        if os.path.isdir(b_path):
            b_coll_root = to_bytes(os.path.join(path, "ansible_collections"))

            if os.path.exists(b_coll_root) and os.path.isdir(b_coll_root):
                coll = None
                if coll_filter is None:
                    namespaces = os.listdir(b_coll_root)
                else:
                    if "." in coll_filter:
                        (nsp, coll) = coll_filter.split(".")
                    else:
                        nsp = coll_filter
                    namespaces = [nsp]

                for ns in namespaces:
                    b_namespace_dir = os.path.join(b_coll_root, to_bytes(ns))

                    if os.path.isdir(b_namespace_dir):
                        if coll is None:
                            colls = os.listdir(b_namespace_dir)
                        else:
                            colls = [coll]

                        for collection in colls:
                            # skip dupe collections as they will be masked in execution
                            if collection not in collections[ns]:
                                b_coll = to_bytes(collection)
                                b_coll_dir = os.path.join(b_namespace_dir, b_coll)
                                if is_collection_path(b_coll_dir):
                                    collections[ns][collection] = b_coll_dir
                                    yield b_coll_dir