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
- If on Google App Engine, upgrade to Python 3 runtime which has full OS support.
- Use a runtime/environment that provides os.mkdir, os.rename, os.unlink.
- Avoid code paths that require resource extraction / directory creation on restricted platforms.
- 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
- Pre-bundle resources when deploying to sandboxed platforms like GAE Standard Python 2.
- Check WRITE_SUPPORT before attempting resource extraction.
- Upgrade to Python 3 runtime on restricted platforms for full OS support.
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
- "os.rename" and "os.unlink" are not supported on this…
- Can not use any platform or abi specific options unless…
- is not a supported wheel on this platform.
- No wheel found matching the provided tags for package
- non-local file URIs are not supported on this platform
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 = NoneView on GitHub (pinned to f399c37189)