pypa/pip · error · ClassNotFound

error when loading custom lexer

Error message

error when loading custom lexer: {err}

What it means

Raised by load_lexer_from_file() as a ClassNotFound when exec(f.read(), custom_namespace) at line 154, or the subsequent instantiation at line 160, raises any exception that is not OSError or ClassNotFound — caught by the generic `except Exception as err` at line 165-166. The inner error is stringified into the message.

Solutions

  1. Open and inspect the inner error in the message — it includes the original exception text (e.g., ImportError: No module named 'foo').
  2. Import and exec the file yourself in a REPL to reproduce the underlying exception with a full traceback before the ClassNotFound wrapper hides it.
  3. Ensure all dependencies the custom lexer file imports are installed and that the file is syntactically valid (python -m py_compile yourfile.py).

Example fix

// before
lexer = load_lexer_from_file('mylexer.py')  # raises "error when loading custom lexer: ImportError ..."

// after
# reproduce raw error first to see full traceback
import ast; ast.parse(open('mylexer.py','rb').read())
# then fix the missing dependency / syntax in mylexer.py
lexer = load_lexer_from_file('mylexer.py')
Defensive patterns

Strategy: try-catch

Validate before calling

import ast
def file_parses(filename):
    try:
        ast.parse(open(filename, 'rb').read(), filename=filename)
        return True
    except SyntaxError:
        return False

Try / catch

from pygments.util import ClassNotFound
try:
    lexer = load_lexer_from_file(path, lexername=name)
except ClassNotFound as e:
    # 'error when loading custom lexer: <inner>' — surface inner cause
    raise RuntimeError(f'custom lexer load failed: {e}') from e

Prevention

When it happens

Trigger: The loaded file contains a Python SyntaxError, ImportError, or NameError; the file defines the class but the class's __init__ or a module-level statement raises during exec; the retrieved class is not callable or fails at lexer_class(**options).

Common situations: The custom lexer file imports another module that is not installed in the environment; the file has an unguarded import that fails at exec time; the lexer class constructor requires arguments not supplied via options; a typo/indentation error in the file.

Related errors


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

Appendix: source

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

    .. 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:
            if _fn_matches(fn, filename):
                if name not in _lexer_cache:
                    _load_lexers(modname)
                matches.append((_lexer_cache[name], filename))

View on GitHub (pinned to f399c37189)