pypa/pip · error · ValueError

SHGetKnownFolderPath returned NULL for {csidl_name}

Error message

SHGetKnownFolderPath returned NULL for {csidl_name}

What it means

Raised as ValueError by the ctypes resolve() when the Windows SHGetKnownFolderPath API call returned a NULL pointer for the given CSIDL/known-folder GUID. A NULL result means the system could not resolve that known folder (e.g. it does not exist on this Windows edition or the user profile lacks it).

Source

Thrown at src/pip/_vendor/platformdirs/windows.py:363

    kernel32.GetShortPathNameW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR, wintypes.DWORD]

    def resolve(csidl_name: str) -> str:
        folder_guid = _KNOWN_FOLDER_GUIDS.get(csidl_name)
        if folder_guid is None:
            msg = f"Unknown CSIDL name: {csidl_name}"
            raise ValueError(msg)

        guid = _GUID()
        ole32.CLSIDFromString(folder_guid, byref(guid))

        path_ptr = wintypes.LPWSTR()
        shell32.SHGetKnownFolderPath(byref(guid), _KF_FLAG_DONT_VERIFY, None, byref(path_ptr))
        result = path_ptr.value
        ole32.CoTaskMemFree(path_ptr)

        if result is None:
            msg = f"SHGetKnownFolderPath returned NULL for {csidl_name}"
            raise ValueError(msg)

        if any(ord(c) > 255 for c in result):  # ruff:ignore[magic-value-comparison]
            buf = create_unicode_buffer(1024)
            if kernel32.GetShortPathNameW(result, buf, 1024):
                result = buf.value

        return result

    return resolve


def get_win_folder_via_ctypes(csidl_name: str) -> str:
    """Get folder via :func:`SHGetKnownFolderPath`.

    See https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath.

    """
    return _build_get_win_folder_via_ctypes()(csidl_name)

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Set the corresponding WIN_PD_OVERRIDE_<NAME> environment variable to a concrete path so platformdirs skips the API call.
  2. Run under an interactive user account whose profile has the folder materialized.
  3. Wrap the call in try/except ValueError and fall back to a sensible default path.

Example fix

# before
path = get_win_folder('CSIDL_DOWNLOADS')  # NULL -> ValueError

# after
try:
    path = get_win_folder('CSIDL_DOWNLOADS')
except ValueError:
    path = os.path.join(os.environ['USERPROFILE'], 'Downloads')
Defensive patterns

Strategy: fallback

Validate before calling

null

Type guard

null

Try / catch

try:
    path = get_win_folder(csidl_name)
except ValueError as e:
    if 'SHGetKnownFolderPath returned NULL' in str(e):
        path = os.path.join(os.environ.get('USERPROFILE',''), default_subdir(csidl_name))
    raise

Prevention

When it happens

Trigger: resolve(csidl_name) calls shell32.SHGetKnownFolderPath with the folder GUID; path_ptr.value comes back None, so the folder path is unavailable on the running Windows system.

Common situations: Server Core or a stripped Windows SKU that does not provide a particular known folder, a corrupted/missing user profile (e.g. Downloads not materialized), or a service account without a populated profile.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/056b9b54b246cadf.json. Report an issue: GitHub.