# Buggy code
def decode_dict(
    data,
    encoding=None,
    errors="strict",
    keep=False,
    normalize=False,
    preserve_dict_class=False,
    preserve_tuples=False,
):
    """
    Decode all string values to Unicode
    """
    # Make sure we preserve OrderedDicts
    rv = data.__class__() if preserve_dict_class else {}
    for key, value in six.iteritems(data):
        if isinstance(key, tuple):
            key = (
                decode_tuple(
                    key, encoding, errors, keep, normalize, preserve_dict_class
                )
                if preserve_tuples
                else decode_list(
                    key,
                    encoding,
                    errors,
                    keep,
                    normalize,
                    preserve_dict_class,
                    preserve_tuples,
                )
            )
        else:
            try:
                key = salt.utils.stringutils.to_unicode(
                    key, encoding, errors, normalize
                )
            except TypeError:
                # to_unicode raises a TypeError when input is not a
                # string/bytestring/bytearray. This is expected and simply
                # means we are going to leave the value as-is.
                pass
            except UnicodeDecodeError:
                if not keep:
                    raise

        if isinstance(value, list):
            value = decode_list(
                value,
                encoding,
                errors,
                keep,
                normalize,
                preserve_dict_class,
                preserve_tuples,
            )
        elif isinstance(value, tuple):
            value = (
                decode_tuple(
                    value, encoding, errors, keep, normalize, preserve_dict_class
                )
                if preserve_tuples
                else decode_list(
                    value,
                    encoding,
                    errors,
                    keep,
                    normalize,
                    preserve_dict_class,
                    preserve_tuples,
                )
            )
        elif isinstance(value, collections.Mapping):
            value = decode_dict(
                value,
                encoding,
                errors,
                keep,
                normalize,
                preserve_dict_class,
                preserve_tuples,
            )
        else:
            try:
                value = salt.utils.stringutils.to_unicode(
                    value, encoding, errors, normalize
                )
            except TypeError:
                # to_unicode raises a TypeError when input is not a
                # string/bytestring/bytearray. This is expected and simply
                # means we are going to leave the value as-is.
                pass
            except UnicodeDecodeError:
                if not keep:
                    raise

        rv[key] = value
    return rv


# Traceback (TypeError)
2018-04-04 05:37:16,969 [salt.loaded.int.pillar.pillar_ldap:262 ][CRITICAL][5777] Failed to retrieve pillar data from LDAP:
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/salt/pillar/pillar_ldap.py", line 256, in _do_search
**connargs)['results']
File "/usr/lib/python2.7/site-packages/salt/modules/ldapmod.py", line 150, in search
results = _ldap.search_s(dn, int(scope), filter, attrs)
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 552, in search_s
return self.search_ext_s(base,scope,filterstr,attrlist,attrsonly,None,None,timeout=self.timeout)
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 545, in search_ext_s
msgid = self.search_ext(base,scope,filterstr,attrlist,attrsonly,serverctrls,clientctrls,timeout,sizelimit)
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 541, in search_ext
timeout,sizelimit,
File "/usr/lib64/python2.7/site-packages/ldap/ldapobject.py", line 99, in _ldap_call
result = func(*args,**kwargs)
TypeError: ('expected string in list', u'uid')

# Fixed code
def decode_dict(
    data,
    encoding=None,
    errors="strict",
    keep=False,
    normalize=False,
    preserve_dict_class=False,
    preserve_tuples=False,
    to_str=False,
):
    """
    Decode all string values to Unicode. Optionally use to_str=True to ensure
    strings are str types and not unicode on Python 2.
    """
    _decode_func = (
        salt.utils.stringutils.to_unicode
        if not to_str
        else salt.utils.stringutils.to_str
    )
    # Make sure we preserve OrderedDicts
    rv = data.__class__() if preserve_dict_class else {}
    for key, value in six.iteritems(data):
        if isinstance(key, tuple):
            key = (
                decode_tuple(
                    key, encoding, errors, keep, normalize, preserve_dict_class, to_str
                )
                if preserve_tuples
                else decode_list(
                    key,
                    encoding,
                    errors,
                    keep,
                    normalize,
                    preserve_dict_class,
                    preserve_tuples,
                    to_str,
                )
            )
        else:
            try:
                key = _decode_func(key, encoding, errors, normalize)
            except TypeError:
                # to_unicode raises a TypeError when input is not a
                # string/bytestring/bytearray. This is expected and simply
                # means we are going to leave the value as-is.
                pass
            except UnicodeDecodeError:
                if not keep:
                    raise

        if isinstance(value, list):
            value = decode_list(
                value,
                encoding,
                errors,
                keep,
                normalize,
                preserve_dict_class,
                preserve_tuples,
                to_str,
            )
        elif isinstance(value, tuple):
            value = (
                decode_tuple(
                    value,
                    encoding,
                    errors,
                    keep,
                    normalize,
                    preserve_dict_class,
                    to_str,
                )
                if preserve_tuples
                else decode_list(
                    value,
                    encoding,
                    errors,
                    keep,
                    normalize,
                    preserve_dict_class,
                    preserve_tuples,
                    to_str,
                )
            )
        elif isinstance(value, collections.Mapping):
            value = decode_dict(
                value,
                encoding,
                errors,
                keep,
                normalize,
                preserve_dict_class,
                preserve_tuples,
                to_str,
            )
        else:
            try:
                value = _decode_func(value, encoding, errors, normalize)
            except TypeError:
                # to_unicode raises a TypeError when input is not a
                # string/bytestring/bytearray. This is expected and simply
                # means we are going to leave the value as-is.
                pass
            except UnicodeDecodeError:
                if not keep:
                    raise

        rv[key] = value
    return rv