# Buggy code
def _available_services(refresh=False):
    """
    This is a helper function for getting the available macOS services.
    """
    try:
        if __context__["available_services"] and not refresh:
            log.debug("Found context for available services.")
            __context__["using_cached_services"] = True
            return __context__["available_services"]
    except KeyError:
        pass

    launchd_paths = [
        "/Library/LaunchAgents",
        "/Library/LaunchDaemons",
        "/System/Library/LaunchAgents",
        "/System/Library/LaunchDaemons",
    ]

    try:
        for user in os.listdir("/Users/"):
            agent_path = "/Users/{}/Library/LaunchAgents/".format(user)
            if os.path.isdir(agent_path):
                launchd_paths.append(agent_path)
    except OSError:
        pass

    _available_services = dict()
    for launch_dir in launchd_paths:
        for root, dirs, files in salt.utils.path.os_walk(launch_dir):
            for file_name in files:
                # Must be a plist file
                if not file_name.endswith(".plist"):
                    continue

                # Follow symbolic links of files in _launchd_paths
                file_path = os.path.join(root, file_name)
                true_path = os.path.realpath(file_path)

                # ignore broken symlinks
                if not os.path.exists(true_path):
                    continue

                try:
                    # This assumes most of the plist files
                    # will be already in XML format
                    if six.PY2:
                        plist = plistlib.readPlist(true_path)
                    else:
                        with salt.utils.files.fopen(true_path, "rb") as plist_handle:
                            plist = plistlib.load(plist_handle)

                except Exception:
                    # If plistlib is unable to read the file we'll need to use
                    # the system provided plutil program to do the conversion
                    cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
                        true_path
                    )
                    plist_xml = __salt__["cmd.run"](cmd)
                    if six.PY2:
                        plist = plistlib.readPlistFromString(plist_xml)
                    else:
                        plist = plistlib.loads(
                            salt.utils.stringutils.to_bytes(plist_xml)
                        )

                try:
                    _available_services[plist.Label.lower()] = {
                        "file_name": file_name,
                        "file_path": true_path,
                        "plist": plist,
                    }
                except AttributeError:
                    # Handle malformed plist files
                    _available_services[os.path.basename(file_name).lower()] = {
                        "file_name": file_name,
                        "file_path": true_path,
                        "plist": plist,
                    }

    # put this in __context__ as this is a time consuming function.
    # a fix for this issue. https://github.com/saltstack/salt/issues/48414
    __context__["available_services"] = _available_services
    # this is a fresh gathering of services, set cached to false
    __context__["using_cached_services"] = False

    return __context__["available_services"]


# Traceback (InvalidFileException)
[ERROR   ] An un-handled exception was caught by salt's global exception handler:
InvalidFileException: Invalid file
Traceback (most recent call last):
File "/opt/salt/lib/python3.5/site-packages/salt/utils/mac_utils.py", line 324, in _available_services
plist = plistlib.readPlist(true_path)
File "/opt/salt/lib/python3.5/plistlib.py", line 164, in readPlist
dict_type=_InternalDict)
File "/opt/salt/lib/python3.5/plistlib.py", line 991, in load
raise InvalidFileException()
plistlib.InvalidFileException: Invalid file

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/opt/salt/bin/salt-call", line 11, in <module>
salt_call()
File "/opt/salt/lib/python3.5/site-packages/salt/scripts.py", line 400, in salt_call
client.run()
File "/opt/salt/lib/python3.5/site-packages/salt/cli/call.py", line 57, in run
caller.run()
File "/opt/salt/lib/python3.5/site-packages/salt/cli/caller.py", line 134, in run
ret = self.call()
File "/opt/salt/lib/python3.5/site-packages/salt/cli/caller.py", line 212, in call
ret['return'] = func(*args, **kwargs)
File "/opt/salt/lib/python3.5/site-packages/salt/modules/mac_service.py", line 406, in enabled
list_(name=name, runas=runas)
File "/opt/salt/lib/python3.5/site-packages/salt/modules/mac_service.py", line 153, in list_
service = _get_service(name)
File "/opt/salt/lib/python3.5/site-packages/salt/modules/mac_service.py", line 64, in _get_service
services = __utils__['mac_utils.available_services']()
File "/opt/salt/lib/python3.5/site-packages/salt/utils/decorators/__init__.py", line 185, in _memoize
cache[args_] = func(*args, **kwargs)
File "/opt/salt/lib/python3.5/site-packages/salt/utils/mac_utils.py", line 368, in available_services
return _available_services()
File "/opt/salt/lib/python3.5/site-packages/salt/utils/mac_utils.py", line 336, in _available_services
salt.utils.stringutils.to_bytes(plist_xml))
File "/opt/salt/lib/python3.5/plistlib.py", line 1006, in loads
fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
File "/opt/salt/lib/python3.5/plistlib.py", line 991, in load
raise InvalidFileException()
plistlib.InvalidFileException: Invalid file
Traceback (most recent call last):
File "/opt/salt/lib/python3.5/site-packages/salt/utils/mac_utils.py", line 324, in _available_services
plist = plistlib.readPlist(true_path)
File "/opt/salt/lib/python3.5/plistlib.py", line 164, in readPlist
dict_type=_InternalDict)
File "/opt/salt/lib/python3.5/plistlib.py", line 991, in load
raise InvalidFileException()
plistlib.InvalidFileException: Invalid file

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/opt/salt/bin/salt-call", line 11, in <module>
salt_call()
File "/opt/salt/lib/python3.5/site-packages/salt/scripts.py", line 400, in salt_call
client.run()
File "/opt/salt/lib/python3.5/site-packages/salt/cli/call.py", line 57, in run
caller.run()
File "/opt/salt/lib/python3.5/site-packages/salt/cli/caller.py", line 134, in run
ret = self.call()
File "/opt/salt/lib/python3.5/site-packages/salt/cli/caller.py", line 212, in call
ret['return'] = func(*args, **kwargs)
File "/opt/salt/lib/python3.5/site-packages/salt/modules/mac_service.py", line 406, in enabled
list_(name=name, runas=runas)
File "/opt/salt/lib/python3.5/site-packages/salt/modules/mac_service.py", line 153, in list_
service = _get_service(name)
File "/opt/salt/lib/python3.5/site-packages/salt/modules/mac_service.py", line 64, in _get_service
services = __utils__['mac_utils.available_services']()
File "/opt/salt/lib/python3.5/site-packages/salt/utils/decorators/__init__.py", line 185, in _memoize
cache[args_] = func(*args, **kwargs)
File "/opt/salt/lib/python3.5/site-packages/salt/utils/mac_utils.py", line 368, in available_services
return _available_services()
File "/opt/salt/lib/python3.5/site-packages/salt/utils/mac_utils.py", line 336, in _available_services
salt.utils.stringutils.to_bytes(plist_xml))
File "/opt/salt/lib/python3.5/plistlib.py", line 1006, in loads
fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
File "/opt/salt/lib/python3.5/plistlib.py", line 991, in load
raise InvalidFileException()
plistlib.InvalidFileException: Invalid file

# Fixed code
def _available_services(refresh=False):
    """
    This is a helper function for getting the available macOS services.

    The strategy is to look through the known system locations for
    launchd plist files, parse them, and use their information for
    populating the list of services. Services can run without a plist
    file present, but normally services which have an automated startup
    will have a plist file, so this is a minor compromise.
    """
    try:
        if __context__["available_services"] and not refresh:
            log.debug("Found context for available services.")
            __context__["using_cached_services"] = True
            return __context__["available_services"]
    except KeyError:
        pass

    launchd_paths = [
        "/Library/LaunchAgents",
        "/Library/LaunchDaemons",
        "/System/Library/LaunchAgents",
        "/System/Library/LaunchDaemons",
    ]

    try:
        for user in os.listdir("/Users/"):
            agent_path = "/Users/{}/Library/LaunchAgents".format(user)
            if os.path.isdir(agent_path):
                launchd_paths.append(agent_path)
    except OSError:
        pass

    _available_services = dict()
    for launch_dir in launchd_paths:
        for root, dirs, files in salt.utils.path.os_walk(launch_dir):
            for file_name in files:
                # Must be a plist file
                if not file_name.endswith(".plist"):
                    continue

                # Follow symbolic links of files in _launchd_paths
                file_path = os.path.join(root, file_name)
                true_path = os.path.realpath(file_path)

                # ignore broken symlinks
                if not os.path.exists(true_path):
                    continue

                try:
                    if six.PY2:
                        # py2 plistlib can't read binary plists, and
                        # uses a different API than py3.
                        plist = plistlib.readPlist(true_path)
                    else:
                        with salt.utils.files.fopen(true_path, "rb") as handle:
                            plist = plistlib.load(handle)

                except plistlib.InvalidFileException:
                    # Raised in python3 if the file is not XML.
                    # There's nothing we can do; move on to the next one.
                    msg = 'Unable to parse "%s" as it is invalid XML: InvalidFileException.'
                    logging.warning(msg, true_path)
                    continue

                except xml.parsers.expat.ExpatError:
                    # Raised by py2 for all errors.
                    # Raised by py3 if the file is XML, but with errors.
                    if six.PY3:
                        # There's an error in the XML, so move on.
                        msg = 'Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.'
                        logging.warning(msg, true_path)
                        continue

                    # Use the system provided plutil program to attempt
                    # conversion from binary.
                    cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(
                        true_path
                    )
                    try:
                        plist_xml = __salt__["cmd.run"](cmd)
                        plist = plistlib.readPlistFromString(plist_xml)
                    except xml.parsers.expat.ExpatError:
                        # There's still an error in the XML, so move on.
                        msg = 'Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.'
                        logging.warning(msg, true_path)
                        continue

                _available_services[plist["Label"].lower()] = {
                    "file_name": file_name,
                    "file_path": true_path,
                    "plist": plist,
                }

    # put this in __context__ as this is a time consuming function.
    # a fix for this issue. https://github.com/saltstack/salt/issues/48414
    __context__["available_services"] = _available_services
    # this is a fresh gathering of services, set cached to false
    __context__["using_cached_services"] = False

    return __context__["available_services"]