Skip to content

'ImportError: DLL load failed while importing xxxxxxx: The filename or extension is too long' with LongPathsEnabled #126929

Description

@Andrej730

Bug report

Bug description:

Python 3.11.9

In Python there there is a known limitation on Windows for paths being limited at 260 symbols, unless LongPathsEnabled is set, see https://docs.python.org/3/using/windows.html#removing-the-max-path-limitation.

Without that registry key set import of module at long path fails with ModuleNotFoundError: No module named 'xxxxxxxxx' or opening file fails with FileNotFoundError: [Errno 2] No such file or directory. With registry key set the limit is extended and 260 symbols long paths are working fine.

The issue I've found that importing binary Python extension seems to break on long paths (250 symbols seems to be max length) even with LongPathsEnabled, see the snippet below.
To reproduce this, you can setup some very long path (>250 symbols) in your site-packages and put in that directory one empty .py file (in my case it's "yyyy...yyyyyyyyyyy.py") and some .pyd file that it's importing fine without any other binary dependencies - in my case I was using _yaml.cp311-win_amd64.pyd I got from https://pypi.org/project/PyYAML/#files.

# Importing just .py module.
import xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy as script
print(len(script.__file__)) # 298. Doesn't seem to have limit at 250-260 symbols.
# Importing .pyd.
import xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx._yaml as yaml
print(len(yaml.__file__)) # 250. seems to be the max.

Found this issue working with Blender (related issue) - Blender comes with it's own Python and there's an option to store some of Blender addons dependencies next to blender.exe, so it can be virtually anywhere on the computer and paths can get very long.

CPython versions tested on:

3.11

Operating systems tested on:

Windows

Linked PRs

Activity

  1. zooba commented on Nov 18, 2024

    @zooba
    Member

    Yes, even with long paths enabled, Windows still has a per-segment limit: (from https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation)

    This type of path is composed of components separated by backslashes, each up to the value returned in the lpMaximumComponentLength parameter of the GetVolumeInformation function (this value is commonly 255 characters)

    So no individual directory or file name can have more than that many characters, even though the entire path might exceed that length. That's just an OS limit, and there's no getting around it.

    Having a quick look through the Blender issue, it does seem that the issue there is some executables probably aren't correctly manifested to allow long paths. It's annoying that you have to do that, but again, that's the way Windows made it work (ironically, the first release of Windows with the long paths feature didn't check the manifests, so everything got it automatically and life was great! 😆 ).

  2. zooba commented on Nov 18, 2024

    @zooba
    Member

    I'm not sure there's anything we can do here, but let's leave the issue open for a couple of days in case someone has an idea. (I don't particularly want to try and generate a better error message than Windows itself, though in this case it's possible because the actual limit is available at runtime.)

  3. Andrej730 commented on Nov 18, 2024

    @Andrej730
    ContributorAuthor

    @zooba

    Yes, even with long paths enabled, Windows still has a per-segment limit: (from learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation)

    This type of path is composed of components separated by backslashes, each up to the value returned in the lpMaximumComponentLength parameter of the GetVolumeInformation function (this value is commonly 255 characters)

    So no individual directory or file name can have more than that many characters, even though the entire path might exceed that length. That's just an OS limit, and there's no getting around it.

    The issue I report here is about .pyd filepaths being limited to around 250 symbols in overall size, not in a single segment.

    Having a quick look through the Blender issue, it does seem that the issue there is some executables probably aren't correctly manifested to allow long paths

    It was indeed the original issue with Blender with importing .py modules that's resolved with changing manifest, resolving helped figuring that there is another issue specifically with .pyd's.

  4. zooba commented on Nov 18, 2024

    @zooba
    Member

    Ah, thanks for clarifying.

    I can reproduce, but it's deep inside Windows's dependency loader. As far as I can tell, it can only be fixed by an OS update.

    It does appear to only occur when the DLL has an embedded manifest. Doesn't seem to matter what's in the manifest, but if it's there then it apparently tries to load something through the path and hits a too-long error.

    I've reached out to the team who owns this part of the OS to see what they say. Removing the manifest entirely may be the right option, but I want to confirm that with the part of the OS that relies on it before making that general advice.

  5. LavaTime commented on Jul 13, 2025

    @LavaTime

    I also experience that,
    Is there any progress from the OS team? Or anyone has other ideas on how to solve this ? I experience this when using Bazel which creates very long path names

    Or are you aware of some bypass to just never use .pyd?

  6. zooba commented on Jul 28, 2025

    @zooba
    Member

    No, no progress here. It seems that the code in question is so old that nobody is confident to update it.

    The best workaround seems to be disabling embedded manifests entirely in PYD builds. That ought to be fine - the manifest is basically only relevant in an EXE these days - but it may require a more advanced build backend to support it.

  7. chengfeng-xie commented on Aug 28, 2026

    @chengfeng-xie

    A possible workaround mentioned in Maximum Path Length Limitation is that, given an absolute and normalized path, we can prepend \\?\ to it to bypass the MAX_PATH limitation:

    The "\\?\" prefix can also be used with paths constructed according to the universal naming convention (UNC). To specify such a path using UNC, use the "\\?\UNC\" prefix. For example, "\\?\UNC\server\share", where "server" is the name of the computer and "share" is the name of the shared folder. These prefixes are not used as part of the path itself. They indicate that the path should be passed to the system with minimal modification, which means that you cannot use forward slashes to represent path separators, or a period to represent the current directory, or double dots to represent the parent directory. Because you cannot use the "\\?\" prefix with a relative path, relative paths are always limited to a total of MAX_PATH characters.

    I tested this on Windows build 26100.9168 and confirmed that this workaround works with LoadLibraryExW. So I created the following patch (agsinst v3.14.6) to help resolve this issue in my TensorFlow build process:

    Details
    diff --git a/Python/dynload_win.c b/Python/dynload_win.c
    index 6324063401e..b45168f229f 100644
    --- a/Python/dynload_win.c
    +++ b/Python/dynload_win.c
    @@ -10,6 +10,11 @@
     #include "patchlevel.h"           // PY_MAJOR_VERSION
     #include <windows.h>
     
    +#include <assert.h>
    +#include <stdint.h>
    +#include <string.h>
    +#include <wchar.h>
    +
     const char *_PyImport_DynLoadFiletab[] = {
         PYD_TAGGED_SUFFIX,
         PYD_UNTAGGED_SUFFIX,
    @@ -145,6 +150,41 @@ static char *GetPythonImport (HINSTANCE hModule)
         return NULL;
     }
     
    +static HMODULE
    +_Py_LoadLibraryExW(LPCWSTR path, HANDLE file, DWORD flags) {
    +    assert(path != NULL);
    +    assert(wcschr(path, L'/') == NULL);
    +    assert(file == NULL);
    +    do {
    +        if (_Py_isabs(path) == 0) {
    +            break;
    +        }
    +        if (path[0] == L'\\') {
    +            // Exclude UNC or device paths.
    +            break;
    +        }
    +        static const wchar_t prefix[] = L"\\\\?\\";
    +        static const size_t prefix_size =
    +                sizeof(prefix) / sizeof(prefix[0]) - 1;
    +        const size_t path_size = wcslen(path);
    +        assert(path_size <= SIZE_MAX - prefix_size - 1);
    +        wchar_t *const extended_path =
    +                PyMem_New(wchar_t, prefix_size + path_size + 1);
    +        if (extended_path == NULL) {
    +            break;
    +        }
    +        memcpy(extended_path, prefix, prefix_size * sizeof(wchar_t));
    +        _Py_normpath(memcpy(extended_path + prefix_size,
    +                            path,
    +                            (path_size + 1) * sizeof(wchar_t)),
    +                     path_size);
    +        const HMODULE module = LoadLibraryExW(extended_path, file, flags);
    +        PyMem_Del(extended_path);
    +        return module;
    +    } while (0);
    +    return LoadLibraryExW(path, file, flags);
    +}
    +
     #ifdef Py_ENABLE_SHARED
     /* Load python3.dll before loading any extension module that might refer
        to it. That way, we can be sure that always the python3.dll corresponding
    @@ -171,7 +211,7 @@ _Py_CheckPython3(void)
             wchar_t *p = wcsrchr(py3path, L'\\');
             if (p) {
                 wcscpy(p + 1, PY3_DLLNAME);
    -            hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
    +            hPython3 = _Py_LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
                 if (hPython3 != NULL) {
                     return 1;
                 }
    @@ -193,7 +233,7 @@ _Py_CheckPython3(void)
         if (config->prefix) {
             wcscpy_s(py3path, MAXPATHLEN, config->prefix);
             if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME, MAXPATHLEN) >= 0) {
    -            hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
    +            hPython3 = _Py_LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
             }
         }
         return hPython3 != NULL;
    @@ -231,11 +271,9 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
                to avoid DLL preloading attacks and enable use of the
                AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
                ensure DLLs adjacent to the PYD are preferred. */
    -        Py_BEGIN_ALLOW_THREADS
    -        hDLL = LoadLibraryExW(wpathname, NULL,
    -                              LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
    -                              LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
    -        Py_END_ALLOW_THREADS
    +        hDLL = _Py_LoadLibraryExW(wpathname, NULL,
    +                                  LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
    +                                  LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
             PyMem_Free(wpathname);
     
     #ifdef MS_WINDOWS_DESKTOP

    PS For the curious, the cause of this issue is that, when loading a DLL with a manifest, a synthesized path is produced by appending .2.Config to the DLL path. If that path exceeds MAX_PATH, the NT_STATUS_NAME_TOO_LONG error is produced by RtlDosPathNameToNtPathName_U_WithStatus. A path prefixed with \\?\ does not go through that function, so everything works as expected. Also note that the error originates from csrss.exe, so kernel-mode debugging has to be used to inspect it.

  8. zooba commented on Aug 31, 2026

    @zooba
    Member

    I think we'd probably accept a PR along those lines, though I'd prefer it to only try modifying the path in the cases where LoadLibraryExW has already failed, and we can confirm that the file exists and is a regular file (which shouldn't hit the manifest issue). If those conditions are met, then try again with \\?\ prefixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions