pypa/pip · error · OSError

"egg_name" is empty. This likely means no egg could be…

Error message

"egg_name" is empty. This likely means no egg could be found from the "module_path".

What it means

Raised by ZipProvider._extract_resource when self.egg_name is empty/falsy. The egg_name is set during _setup_prefix by walking parent paths looking for a directory matching the .egg naming convention; if none is found, egg_name stays empty. This means the zip-backed resource provider cannot construct a cache path for extraction. Extraction from a bare .zip (not an .egg) is fundamentally unsupported.

Solutions

  1. Package the resource inside a properly named .egg or wheel (.whl) archive so the egg_name can be derived from the path.
  2. Use an alternative resource access API (e.g. resource_string / resource_stream) that does not require extraction to the filesystem.
  3. Ensure the archive filename matches the convention ProjectName-version-pyX.Y.egg so _is_egg_path returns True during _setup_prefix.
  4. If you control the packaging, switch to wheel format which pkg_resources recognizes via its dist-info/egg-info metadata.

Example fix

// before (resource lives in plain data.zip on sys.path)
from pkg_resources import resource_filename
path = resource_filename('mypkg', 'data.csv')  # raises OSError

// after (repackage as wheel/egg, or read from stream)
from pkg_resources import resource_string
data = resource_string('mypkg', 'data.csv')
Defensive patterns

Strategy: validation

Validate before calling

from pkg_resources import ZipProvider
provider = resource_manager(provider_of_module)
if isinstance(provider, ZipProvider) and not getattr(provider, 'egg_name', ''):
    # cannot extract; use stream access instead
    data = resource_string('mypkg', 'data.csv')
else:
    path = resource_filename('mypkg', 'data.csv')

Type guard

import re
def is_valid_egg_path(path: str) -> bool:
    return bool(re.match(r'^[\w.]+-\d[^-]+-py\d', os.path.basename(path)))

Try / catch

from pkg_resources import resource_filename
try:
    path = resource_filename('mypkg', 'data.csv')
except OSError:
    # fallback: read as string
    data = resource_string('mypkg', 'data.csv')

Prevention

When it happens

Trigger: Calling resource_filename() or get_resource_filename() on a resource that lives inside a plain .zip archive rather than an .egg/whl, or when the module_path does not have a recognizable egg ancestor directory. The check at line 2046 (`if not self.egg_name`) fires inside _extract_resource which is reached via get_resource_filename -> _extract_resource.

Common situations: Bundling dependencies as raw .zip files on sys.path instead of .egg or .whl; running on an import hook that registers a zip importer without egg-style naming; legacy packaging tools that produced non-egg zip archives; calling pkg_resources.resource_filename on a module loaded from a zip.

Related errors


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

Appendix: source

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

        return timestamp, size

    # FIXME: 'ZipProvider._extract_resource' is too complex (12)
    def _extract_resource(self, manager: ResourceManager, zip_path) -> str:  # noqa: C901
        if zip_path in self._index():
            for name in self._index()[zip_path]:
                last = self._extract_resource(manager, os.path.join(zip_path, name))
            # return the extracted directory name
            return os.path.dirname(last)

        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])

        if not WRITE_SUPPORT:
            raise OSError(
                '"os.rename" and "os.unlink" are not supported on this platform'
            )
        try:
            if not self.egg_name:
                raise OSError(
                    '"egg_name" is empty. This likely means no egg could be found from the "module_path".'
                )
            real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path))

            if self._is_current(real_path, zip_path):
                return real_path

            outf, tmpnam = _mkstemp(
                ".$extract",
                dir=os.path.dirname(real_path),
            )
            os.write(outf, self.loader.get_data(zip_path))
            os.close(outf)
            utime(tmpnam, (timestamp, timestamp))
            manager.postprocess(tmpnam, real_path)

            try:
                rename(tmpnam, real_path)

View on GitHub (pinned to f399c37189)