lllyasviel/Fooocus · error · TypeError

"suffix" must be a string or tuple of strings

Error message

"suffix" must be a string or tuple of strings

What it means

scandir()'s suffix parameter must be a str or a tuple of str (or None for no filtering). Passing any other type — most commonly a list — raises TypeError immediately, because os.path.endswith only accepts str/tuple. This mirrors mmcv's scandin helper semantics.

Source

Thrown at extras/facexlib/utils/misc.py:96

    return cached_file


def scandir(dir_path, suffix=None, recursive=False, full_path=False):
    """Scan a directory to find the interested files.
    Args:
        dir_path (str): Path of the directory.
        suffix (str | tuple(str), optional): File suffix that we are
            interested in. Default: None.
        recursive (bool, optional): If set to True, recursively scan the
            directory. Default: False.
        full_path (bool, optional): If set to True, include the dir_path.
            Default: False.
    Returns:
        A generator for all the interested files with relative paths.
    """

    if (suffix is not None) and not isinstance(suffix, (str, tuple)):
        raise TypeError('"suffix" must be a string or tuple of strings')

    root = dir_path

    def _scandir(dir_path, suffix, recursive):
        for entry in os.scandir(dir_path):
            if not entry.name.startswith('.') and entry.is_file():
                if full_path:
                    return_path = entry.path
                else:
                    return_path = osp.relpath(entry.path, root)

                if suffix is None:
                    yield return_path
                elif return_path.endswith(suffix):
                    yield return_path
            else:
                if recursive:
                    yield from _scandir(entry.path, suffix=suffix, recursive=recursive)

View on GitHub (pinned to ae05379cc9)

Solutions

  1. Convert to tuple: scandir(dir, suffix=('.png', '.jpg')) or scandir(dir, tuple(ext_list)).
  2. For a single extension, pass a bare string: suffix='.png'.
  3. Pass suffix=None to accept all files and filter afterwards.
  4. If the list comes from YAML/JSON config, coerce with tuple(cfg['extensions']) at the call site.

Example fix

# before
files = list(scandir(img_dir, suffix=['.png', '.jpg']))

# after
files = list(scandir(img_dir, suffix=('.png', '.jpg')))
Defensive patterns

Strategy: type-guard

Validate before calling

if suffix is not None and not isinstance(suffix, (str, tuple)):
    suffix = tuple(suffix)  # coerce lists from YAML/JSON configs
assert suffix is None or isinstance(suffix, (str, tuple))

Type guard

def is_valid_suffix(s) -> bool:
    return s is None or isinstance(s, str) or (isinstance(s, tuple) and all(isinstance(x, str) for x in s))

Try / catch

try:
    files = list(scandir(dir_path, suffix=suffix))
except TypeError as e:
    raise ValueError(f'Invalid suffix {suffix!r}: use str or tuple of str') from e

Prevention

When it happens

Trigger: Calling scandir(dir_path, suffix=['.png', '.jpg']) (list instead of tuple), suffix=('.png',) is fine but suffix=b'.png' or suffix=Path('.png') is not. Also triggered by helpers that forward a user-supplied extension list verbatim.

Common situations: Refactoring code that used glob/glob2 and passing list literals; config files that define extensions as YAML lists which arrive as Python lists.

Related errors


AI-assisted analysis of lllyasviel/Fooocus@ae05379cc9 (2026-08-15). Data as JSON: /api/errors/92a1c3b57870fd1c. Report an issue: GitHub.