nvbn/thefuck · error · ValueError

Path separators not allowed in script names

Error message

Path separators not allowed in script names

What it means

This ValueError is raised by fastentrypoints, a package that monkey-patches setuptools' easy_install.ScriptWriter.get_args to generate fast, importlib-based console scripts instead of pkg_resources-based ones. During wheel/build processing it iterates over every entry point in the 'console_scripts' and 'gui_scripts' groups and rejects any whose name contains a forward or back slash. The check exists because a script name with a path separator would cause the generated executable wrapper to be written into unintended directories (or escape the build output tree).

Source

Thrown at fastentrypoints.py:68

    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit({2}())'''


@classmethod
def get_args(cls, dist, header=None):
    """
    Yield write_script() argument tuples for a distribution's
    console_scripts and gui_scripts entry points.
    """
    if header is None:
        header = cls.get_header()
    spec = str(dist.as_requirement())
    for type_ in 'console', 'gui':
        group = type_ + '_scripts'
        for name, ep in dist.get_entry_map(group).items():
            # ensure_safe_name
            if re.search(r'[\\/]', name):
                raise ValueError("Path separators not allowed in script names")
            script_text = TEMPLATE.format(
                          ep.module_name, ep.attrs[0], '.'.join(ep.attrs),
                          spec, group, name)
            args = cls._get_script_args(type_, name, header, script_text)
            for res in args:
                yield res


easy_install.ScriptWriter.get_args = get_args


def main():
    import os
    import re
    import shutil
    import sys
    dests = sys.argv[1:] or ['.']
    filename = re.sub(r'\.pyc$', '.py', __file__)

View on GitHub (pinned to c7e7e1d884)

Solutions

  1. Inspect your project's entry_points declaration (setup.py, setup.cfg [options.entry_points], or pyproject.toml [project.scripts]) and remove any '/' or '\\' from the script name, e.g. change 'bin/tool = mymod:main' to 'tool = mymod:main'.
  2. If you intend the script to live in a subdirectory of the environment's bin/, note that entry-point names must be plain command names; pick a unique flat name instead (e.g. 'myproject-tool').
  3. Verify the entry_points string is well formed (correct '=' between name and target, one entry per line in setup.cfg) so the name field is not accidentally picking up a path fragment.
  4. If the offending name comes from a third-party dependency being built from source, report it upstream; as a local workaround you can uninstall/omit fastentrypoints so stock setuptools ScriptWriter is used (which sanitizes differently) while you patch the metadata.

Example fix

# before (setup.py)
entry_points={
    'console_scripts': [
        'bin/mytool = myproject.cli:main',
    ],
}

# after
entry_points={
    'console_scripts': [
        'mytool = myproject.cli:main',
    ],
}
Defensive patterns

Strategy: validation

Validate before calling

# In setup.py, before declaring entry points, validate every script name:
import re

ENTRY_POINTS = {
    'console_scripts': [
        'bin/mytool = myproject.cli:main',  # would fail
    ],
}

for group, entries in ENTRY_POINTS.items():
    for entry in entries:
        name = entry.split('=', 1)[0].strip()
        if re.search(r'[\\/]', name):
            raise SystemExit(
                f"Invalid entry point name {name!r} in {group}: "
                "path separators are not allowed (fastentrypoints)")

Type guard

# Python: assert entry-point names are flat command names before build
import re

def is_safe_script_name(name: str) -> bool:
    """True if name is a valid console_scripts name for fastentrypoints."""
    return bool(name) and not re.search(r'[\\/]', name) and '=' not in name

assert all(is_safe_script_name(e.split('=')[0].strip())
           for e in CONSOLE_SCRIPT_ENTRIES)

Try / catch

# If you drive builds programmatically, catch and surface the real entry point:
from setuptools.command.easy_install import easy_install

try:
    # build/install path that goes through ScriptWriter.get_args
    run_setup_command('bdist_wheel')
except ValueError as e:
    if 'Path separators not allowed' in str(e):
        raise SystemExit('Fix entry_points: script names must not contain / or \\') from e
    raise

Prevention

When it happens

Trigger: Calling setup.py / setuptools build machinery (e.g. `python setup.py install`, bdist_wheel, or pip installing a project) that has imported fastentrypoints, when the project's setup() declares entry_points like {'console_scripts': ['bin/tool = mymod:main']} or {'gui_scripts': ['sub/dir/app = mymod:main']}. The regex r'[\\/]' matches the slash in the entry-point NAME (the part before '='), triggering the raise at fastentrypoints.py:68.

Common situations: Typos in setup.py/setup.cfg/pyproject.toml entry_points where a path-like name is written instead of a plain command name; migrating a package whose original scripts lived in a subdirectory and copying that layout into the entry-point name; name fields containing Windows-style backslashes; occasionally a corrupted or mis-parsed entry_points string where '=' or newlines are misplaced so the wrong token is treated as the name.


AI-assisted analysis of nvbn/thefuck@c7e7e1d884 (2026-08-14). Data as JSON: /api/errors/460c42fe909f5fb6. Report an issue: GitHub.