pypa/pip · error · OSError

"os.rename" and "os.unlink" are not supported on this…

Error message

"os.rename" and "os.unlink" are not supported on this platform

What it means

Raised as OSError by ZipProvider._extract_resource() when the module-level WRITE_SUPPORT flag is False. That flag is set False if `from os import mkdir, rename, unlink` raises ImportError at import time — which happens on platforms without these OS primitives (e.g. Google App Engine classic sandbox). Extraction requires os.rename/os.unlink to atomically write temp files, so it is unsupported.

Solutions

  1. Avoid resource_filename()/extraction on write-restricted platforms; use resource_stream()/resource_string() to read directly.
  2. Pre-extract resources to disk at deploy/build time so no runtime extraction is needed.
  3. Move to a runtime with full os support, or use importlib.resources which can serve zip contents without extraction.

Example fix

// before
# on a platform without os.rename/os.unlink
path = resource_filename('mypkg', 'data.txt')  # raises OSError
// after
# read directly from the archive, no extraction
stream = resource_stream('mypkg', 'data.txt')
Defensive patterns

Strategy: fallback

Validate before calling

import os
try:
    from os import mkdir, rename, unlink
    WRITE_OK = True
except ImportError:
    WRITE_OK = False
if not WRITE_OK:
    # use stream-based access; extraction unavailable

Try / catch

try:
    path = resource_filename('pkg', name)
except OSError:
    stream = resource_stream('pkg', name)  # fallback: no extraction

Prevention

When it happens

Trigger: Triggering resource extraction from a zip/egg on a platform where os.mkdir/os.rename/os.unlink are unavailable — historically Google App Engine's Python 2 sandbox or similar restricted runtimes.

Common situations: Deploying pkg_resources-based code to a restricted sandbox (GAE classic) that blocks filesystem write/rename/unlink; exotic embedded runtimes lacking full os support.

Related errors


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

Appendix: source

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

        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

    # 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)

View on GitHub (pinned to f399c37189)