pypa/pip · error · ClassNotFound

cannot read

Error message

cannot read {filename}: {err}

What it means

Raised by load_lexer_from_file() as a ClassNotFound (wrapping the OSError) when open(filename, 'rb') fails at line 153, caught by the `except OSError as err` branch at line 161-162. It indicates the file path itself could not be opened or read by the OS.

Solutions

  1. Verify the file exists with os.path.isfile(filename) before calling, and print os.path.abspath(filename) to confirm the resolved path.
  2. Use an absolute path instead of a relative one, since load_lexer_from_file resolves relative to cwd which may differ from what you expect.
  3. Check filesystem permissions on the target file (read bit must be set for the running user).

Example fix

// before
lexer = load_lexer_from_file('custom_lexer.py')

// after
import os
path = os.path.join(os.path.dirname(__file__), 'custom_lexer.py')
lexer = load_lexer_from_file(path)
Defensive patterns

Strategy: validation

Validate before calling

import os
def path_readable(filename):
    return os.path.isfile(filename) and os.access(filename, os.R_OK)

Try / catch

from pygments.util import ClassNotFound
try:
    lexer = load_lexer_from_file(path)
except ClassNotFound as e:
    if 'cannot read' in str(e):
        # missing/unreadable file — handle gracefully
        lexer = None
    raise

Prevention

When it happens

Trigger: Calling load_lexer_from_file() with a path to a nonexistent file, a directory instead of a file, or a file without read permissions. Any OSError from open() or f.read() lands here.

Common situations: Typo or wrong relative path (the function resolves relative to cwd, not sys.path or the package directory); pointing at a directory; file owned by another user with mode 000; path containing the right name but wrong case on case-sensitive filesystems.

Related errors


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

Appendix: source

Thrown at src/pip/_vendor/pygments/lexers/__init__.py:162

    is equivalent to running eval on the input file.

    Raises ClassNotFound if there are any problems importing the Lexer.

    .. 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 `lexername` from that namespace
        if lexername not in custom_namespace:
            raise ClassNotFound(f'no valid {lexername} class found in {filename}')
        lexer_class = custom_namespace[lexername]
        # And finally instantiate it with the options
        return lexer_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 lexer: {err}')


def find_lexer_class_for_filename(_fn, code=None):
    """Get a lexer for a filename.

    If multiple lexers match the filename pattern, use ``analyse_text()`` to
    figure out which one is more appropriate.

    Returns None if not found.
    """
    matches = []
    fn = basename(_fn)
    for modname, name, _, filenames, _ in LEXERS.values():
        for filename in filenames:

View on GitHub (pinned to f399c37189)