pypa/pip · error · ClassNotFound

cannot read

Error message

cannot read {filename}: {err}

What it means

Raised by load_formatter_from_file when open(filename) raises OSError (file missing, permission denied, not a file, etc.). The OSError is caught and re-raised as ClassNotFound with the original error message at __init__.py:110-111. This is a filesystem/permission problem, not a content problem.

Solutions

  1. Verify the path exists and is a readable file with os.path.isfile before calling.
  2. Use an absolute path to avoid working-directory ambiguity.
  3. Fix file permissions (chmod) if access is denied.

Example fix

# before
load_formatter_from_file('formatters/myfmt.py')
# after
import os
p = os.path.abspath('formatters/myfmt.py')
load_formatter_from_file(p)
Defensive patterns

Strategy: validation

Validate before calling

import os
if not os.path.isfile(path):
    raise FileNotFoundError(f'not a readable file: {path}')

Type guard

def is_readable_file(path: str) -> bool:
    import os
    return os.path.isfile(path) and os.access(path, os.R_OK)

Try / catch

from pip._vendor.pygments.util import ClassNotFound
try:
    fmt = load_formatter_from_file(path)
except ClassNotFound as e:
    if 'cannot read' in str(e):
        raise FileNotFoundError(path) from e
    raise

Prevention

When it happens

Trigger: Passing a path that does not exist, a directory path, or a file without read permission to load_formatter_from_file; relative path resolved against an unexpected working directory.

Common situations: Wrong working directory; typo in path; file deleted between checks; read permissions stripped; passing a URL instead of a local path.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/6c1c96a1e4bc7563. Report an issue: GitHub.

Appendix: source

Thrown at src/pip/_vendor/pygments/formatters/__init__.py:111

    :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading
    the formatter.

    .. versionadded:: 2.2
    """
    try:
        # This empty dict will contain the namespace for the exec'd file
        custom_namespace = {}
        with open(filename, 'rb') as f:
            exec(f.read(), custom_namespace)
        # Retrieve the class `formattername` from that namespace
        if formattername not in custom_namespace:
            raise ClassNotFound(f'no valid {formattername} class found in {filename}')
        formatter_class = custom_namespace[formattername]
        # And finally instantiate it with the options
        return formatter_class(**options)
    except OSError as err:
        raise ClassNotFound(f'cannot read {filename}: {err}')
    except ClassNotFound:
        raise
    except Exception as err:
        raise ClassNotFound(f'error when loading custom formatter: {err}')


def get_formatter_for_filename(fn, **options):
    """
    Return a :class:`.Formatter` subclass instance that has a filename pattern
    matching `fn`. The formatter is given the `options` at its instantiation.

    Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename
    is found.
    """
    fn = basename(fn)
    for modname, name, _, filenames, _ in FORMATTERS.values():
        for filename in filenames:
            if _fn_matches(fn, filename):

View on GitHub (pinned to f399c37189)