pypa/pip · error · ResolutionError

Script {script!r} not found in metadata at {self.egg_info!r}

Error message

Script {script!r} not found in metadata at {self.egg_info!r}

What it means

Raised as a ResolutionError by Distribution.run_script() when the requested script_name does not correspond to an entry under EGG-INFO/scripts/ in the distribution's metadata. run_script() builds 'scripts/' + script_name and checks has_metadata(); if absent it refuses to exec. The distribution must therefore carry script files in its egg-info metadata for the name to resolve.

Source

Thrown at src/pip/_vendor/pkg_resources/__init__.py:1701

    def resource_isdir(self, resource_name: str):
        return self._isdir(self._fn(self.module_path, resource_name))

    def metadata_isdir(self, name: str) -> bool:
        return bool(self.egg_info and self._isdir(self._fn(self.egg_info, name)))

    def resource_listdir(self, resource_name: str):
        return self._listdir(self._fn(self.module_path, resource_name))

    def metadata_listdir(self, name: str) -> list[str]:
        if self.egg_info:
            return self._listdir(self._fn(self.egg_info, name))
        return []

    def run_script(self, script_name: str, namespace: dict[str, Any]):
        script = 'scripts/' + script_name
        if not self.has_metadata(script):
            raise ResolutionError(
                "Script {script!r} not found in metadata at {self.egg_info!r}".format(
                    **locals()
                ),
            )

        script_text = self.get_metadata(script).replace('\r\n', '\n')
        script_text = script_text.replace('\r', '\n')
        script_filename = self._fn(self.egg_info, script)
        namespace['__file__'] = script_filename
        if os.path.exists(script_filename):
            source = _read_utf8_with_fallback(script_filename)
            code = compile(source, script_filename, 'exec')
            exec(code, namespace, namespace)
        else:
            from linecache import cache

            cache[script_filename] = (
                len(script_text),

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Verify the script exists first: `if dist.has_metadata('scripts/' + name): dist.run_script(name, ns)`.
  2. List available scripts via `dist.metadata_listdir('scripts')` and use an exact name from that list.
  3. Prefer EntryPoint.load() over run_script() for invoking console entry points, which is the modern, metadata-driven path.

Example fix

// before
dist.run_script('manage', ns)  # ResolutionError if 'scripts/manage' missing

// after
ep = dist.get_entry_info('console_scripts', 'manage')
ep.load(require=False)(ns)  # use the entry point instead
Defensive patterns

Strategy: validation

Validate before calling

import pkg_resources

def safe_run_script(dist, script_name, namespace):
    script_rel = 'scripts/' + script_name
    if dist.has_metadata(script_rel):
        return dist.run_script(script_name, namespace)
    available = dist.metadata_listdir('scripts') if dist.egg_info else []
    raise KeyError(f'{script_name!r} not in scripts/; available: {available}')

Type guard

def script_exists(dist, script_name):
    return dist.has_metadata('scripts/' + script_name)

Try / catch

try:
    dist.run_script(name, ns)
except pkg_resources.ResolutionError as e:
    if 'not found in metadata' in str(e):
        # script missing; list what is available
        avail = dist.metadata_listdir('scripts')
        raise KeyError(f'missing {name!r}; have {avail}') from e
    raise

Prevention

When it happens

Trigger: Calling dist.run_script('my_script', namespace_dict) where 'my_script' is not present as EGG-INFO/scripts/my_script in that distribution's metadata (typo, wrong distribution, or scripts were never installed/packaged).

Common situations: Calling console-script entry points manually via run_script with the wrong name, a distribution installed without its scripts directory (stripped wheel, broken metadata), or a stale egg-info left after a version change.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/d1068abdae694a2d.json. Report an issue: GitHub.