{"record":{"id":"5ed05a3a6bdfacd7","repo":"pypa/pip","slug":"invalid-script-entry-point-entry-point-a-call","errorCode":null,"errorMessage":"Invalid script entry point: {entry_point} - A callable suffix is required. See https://packaging.python.org/specifications/entry-points/#use-for-scripts for more information.","messagePattern":"Invalid script entry point: (.+?) - A callable suffix is required\\. See https://packaging\\.python\\.org/specifications/entry-points/#use-for-scripts for more information\\.","errorType":"exception","errorClass":"MissingCallableSuffix","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/operations/install/wheel.py","lineNumber":409,"sourceCode":"\n\nclass MissingCallableSuffix(InstallationError):\n    def __init__(self, entry_point: str) -> None:\n        super().__init__(\n            f\"Invalid script entry point: {entry_point} - A callable \"\n            \"suffix is required. See https://packaging.python.org/\"\n            \"specifications/entry-points/#use-for-scripts for more \"\n            \"information.\"\n        )\n\n\ndef _raise_for_invalid_entrypoint(specification: str, scripts_dir: str) -> None:\n    entry = get_export_entry(specification)\n    if entry is None:\n        return\n\n    if entry.suffix is None:\n        raise MissingCallableSuffix(str(entry))\n\n    # distlib joins the entry point name onto the scripts directory, so a name\n    # with path separators or ``..`` components can resolve elsewhere. The script\n    # must resolve to a path strictly inside the scripts directory.\n    dest = os.path.join(scripts_dir, entry.name)\n    resolves_to_scripts_dir = os.path.abspath(dest) == os.path.abspath(scripts_dir)\n    if resolves_to_scripts_dir or not is_within_directory(scripts_dir, dest):\n        raise InstallationError(\n            f\"Invalid script entry point name {entry.name!r}: the script \"\n            f\"would be installed outside the scripts directory ({scripts_dir}).\"\n        )\n\n\nclass PipScriptMaker(ScriptMaker):\n    # Override distlib's default script template with one that\n    # doesn't import `re` module, allowing scripts to load faster.\n    script_template = textwrap.dedent(\"\"\"\\\n        import sys","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/operations/install/wheel.py#L391-L427","documentation":"Raised as MissingCallableSuffix (subclass of InstallationError) by _raise_for_invalid_entrypoint when a wheel's console_scripts entry point specification lacks the ':callable' suffix. Entry points used for executable scripts must name a callable object ('module:func'); a bare 'module' is invalid and pip refuses to generate the script.","triggerScenarios":"Installing a wheel whose entry_points.txt (or pyproject/setup.cfg console_scripts) has an entry without ':', e.g. 'mycli = mypackage.cli' instead of 'mycli = mypackage.cli:main'. distlib's get_export_entry returns an entry whose .suffix is None.","commonSituations":"Hand-edited entry_points; packaging tools/old setuptools emitting malformed entry points; tutorial copy-paste that omitted the ':func' part.","solutions":["Fix the entry point to include the callable: 'name = package.module:func'.","Rebuild the wheel with the corrected entry_points.","Inspect the wheel's '*.dist-info/entry_points.txt' to confirm the ':func' suffix."],"exampleFix":"# before\n[console_scripts]\nmycli = mypackage.cli\n\n# after\n[console_scripts]\nmycli = mypackage.cli:main","handlingStrategy":"validation","validationCode":"from pip._internal.operations.install.wheel import _raise_for_invalid_entrypoint\n\ndef entry_point_ok(spec: str, scripts_dir: str) -> bool:\n    try:\n        _raise_for_invalid_entrypoint(spec, scripts_dir)\n        return True\n    except Exception:\n        return False\n\n# assert entry_point_ok(\"pkg.mod:main\", \"/tmp/scripts\")\n# assert not entry_point_ok(\"pkg.mod\", \"/tmp/scripts\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use the 'module:callable' form for console_scripts entries.","Validate entry_points with a standard build backend before publishing wheels.","Inspect *.dist-info/entry_points.txt when debugging script generation."],"tags":["packaging","wheel","entry-points","scripts"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}