pypa/pip · error · NotImplementedError

resource_filename() only supported for .egg, not .zip

Error message

resource_filename() only supported for .egg, not .zip

What it means

Raised by ZipProvider.get_resource_filename() when self.egg_name is empty/falsy. ZipProvider extracts zip-archived resources to a cache directory keyed by the egg name; without an egg name there is no stable extraction directory, so it refuses. This indicates the resource lives in a plain .zip rather than a named .egg, which this code path does not support for filesystem extraction.

Solutions

  1. Use resource_stream()/resource_string() instead of resource_filename() to read zip contents without extraction.
  2. Package the code as an .egg (with EGG-INFO) so egg_name is populated.
  3. Install the package normally on the filesystem rather than running from a bare .zip.

Example fix

// before
# module loaded from plain .zip, no egg_name -> NotImplementedError
path = resource_filename('mypkg', 'data.txt')
// after
# read bytes directly without extraction
data = resource_string('mypkg', 'data.txt')
Defensive patterns

Strategy: fallback

Validate before calling

# Check if provider is a ZipProvider without egg_name before calling resource_filename
from pkg_resources import ZipProvider
if isinstance(provider, ZipProvider) and not provider.egg_name:
    # use stream-based access instead
    data = resource_string('pkg', name)

Try / catch

try:
    path = resource_filename('pkg', name)
except NotImplementedError:
    data = resource_string('pkg', name)  # fallback: read bytes

Prevention

When it happens

Trigger: Calling resource_filename() (which routes to get_resource_filename) for a package imported from a .zip archive that is not an .egg and has no egg name set, so self.egg_name is ''.

Common situations: Packaging code in a plain .zip and adding it to sys.path; an import hook that serves modules from a zip without egg metadata; legacy zipped modules without EGG-INFO.

Related errors


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

Appendix: source

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

        if fspath.startswith(self.zip_pre):
            return fspath[len(self.zip_pre) :]
        raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre))

    def _parts(self, zip_path):
        # Convert a zipfile subpath into an egg-relative path part list.
        # pseudo-fs path
        fspath = self.zip_pre + zip_path
        if fspath.startswith(self.egg_root + os.sep):
            return fspath[len(self.egg_root) + 1 :].split(os.sep)
        raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root))

    @property
    def zipinfo(self):
        return self._zip_manifests.load(self.loader.archive)

    def get_resource_filename(self, manager: ResourceManager, resource_name: str):
        if not self.egg_name:
            raise NotImplementedError(
                "resource_filename() only supported for .egg, not .zip"
            )
        # no need to lock for extraction, since we use temp names
        zip_path = self._resource_to_zip(resource_name)
        eagers = self._get_eager_resources()
        if '/'.join(self._parts(zip_path)) in eagers:
            for name in eagers:
                self._extract_resource(manager, self._eager_to_zip(name))
        return self._extract_resource(manager, zip_path)

    @staticmethod
    def _get_date_and_size(zip_stat):
        size = zip_stat.file_size
        # ymdhms+wday, yday, dst
        date_time = zip_stat.date_time + (0, 0, -1)
        # 1980 offset already done
        timestamp = time.mktime(date_time)
        return timestamp, size

View on GitHub (pinned to f399c37189)