pypa/pip · critical · OSError

"os.mkdir" not supported on this platform.

Error message

"os.mkdir" not supported on this platform.

What it means

Raised as OSError in _bypass_ensure_directory() at line 3510 when WRITE_SUPPORT is False. WRITE_SUPPORT is set to False when `from os import mkdir, rename, unlink` raises ImportError (line 82-88), which happens on platforms that lack these OS primitives — most notably Google App Engine Standard Environment's Python 2 sandbox.

Solutions

  1. If on Google App Engine, upgrade to Python 3 runtime which has full OS support.
  2. Use a runtime/environment that provides os.mkdir, os.rename, os.unlink.
  3. Avoid code paths that require resource extraction / directory creation on restricted platforms.
  4. Pre-install/extract resources before deploying to the sandbox.

Example fix

// before: running on GAE Python 2 sandbox
from pkg_resources import resource_filename
resource_filename('pkg', 'data.txt')  # OSError: os.mkdir not supported

// after: deploy with resources pre-extracted, or move to Python 3 runtime
import os
if os.environ.get('GAE_VERSION'):
    data = bundled_data  # pre-bundled at deploy time
else:
    from pkg_resources import resource_filename
    data = open(resource_filename('pkg', 'data.txt')).read()
Defensive patterns

Strategy: type-guard

Validate before calling

from pip._vendor.pkg_resources import WRITE_SUPPORT
def can_extract_resources() -> bool:
    return bool(WRITE_SUPPORT)

Type guard

import sys
def has_write_support() -> bool:
    try:
        from os import mkdir, rename, unlink
        return True
    except ImportError:
        return False

Try / catch

from pip._vendor.pkg_resources import WRITE_SUPPORT
if not WRITE_SUPPORT:
    # use pre-bundled data instead of extraction
    data = bundled_data
else:
    from pkg_resources import resource_filename
    data = open(resource_filename('pkg', 'data.txt')).read()

Prevention

When it happens

Trigger: Any code path that triggers _bypass_ensure_directory (a sandbox-bypassing mkdir) on a platform where os.mkdir/os.rename/os.unlink are not importable. This is checked at import time and propagated as OSError at runtime.

Common situations: Running pkg_resources on Google App Engine Standard (Python 2 sandbox) or other restricted sandboxes; embedded Python environments with stripped OS modules; very old or minimal Python builds.

Related errors


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

Appendix: source

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

    types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob))))
    for t in types:
        if t in registry:
            return registry[t]
    # _find_adapter would previously return None, and immediately be called.
    # So we're raising a TypeError to keep backward compatibility if anyone depended on that behaviour.
    raise TypeError(f"Could not find adapter for {registry} and {ob}")


def ensure_directory(path: StrOrBytesPath):
    """Ensure that the parent directory of `path` exists"""
    dirname = os.path.dirname(path)
    os.makedirs(dirname, exist_ok=True)


def _bypass_ensure_directory(path):
    """Sandbox-bypassing version of ensure_directory()"""
    if not WRITE_SUPPORT:
        raise OSError('"os.mkdir" not supported on this platform.')
    dirname, filename = split(path)
    if dirname and filename and not isdir(dirname):
        _bypass_ensure_directory(dirname)
        try:
            mkdir(dirname, 0o755)
        except FileExistsError:
            pass


def split_sections(s: _NestedStr) -> Iterator[tuple[str | None, list[str]]]:
    """Split a string or iterable thereof into (section, content) pairs

    Each ``section`` is a stripped version of the section header ("[section]")
    and each ``content`` is a list of stripped lines excluding blank lines and
    comment-only lines.  If there are any such lines before the first section
    header, they're returned in a first ``section`` of ``None``.
    """
    section = None

View on GitHub (pinned to f399c37189)