Skip to content

Spaces at the end of file and directory names are silently ignored on windows #115104

Description

@timhoffm

Bug report

Bug description:

>>> import os
>>> os.path.exists("test")
False
>>> os.path.exists("test ")
False
>>> os.mkdir("test ")
>>> os.path.exists("test")
True
>>> os.path.exists("test ")
True

The explorer also shows the created directory as test, i.e. without trailing spaces.

The win32 docs say about trailing spaces

Do not end a file or directory name with a space or a period. Although the underlying file system may support such names, the Windows shell and user interface does not.

One could discuss whether this is user-responsibility, but silently dropping trailing spaces feels wrong. Either create the directory (though not recommended), or raise if there is a space at the end.

Note: Behaviour gets even more weird when combining trailing spaces with alternate data streams, e.g. "test : foo" (matplotlib/matplotlib#27748), which brought me here. But before going into this more difficult discussion, I'd like to learn the general position on handling trailings spaces in windows filenames.

CPython versions tested on:

3.12

Operating systems tested on:

Windows

Activity

  1. zooba commented on Feb 6, 2024

    @zooba
    Member

    This is normal Windows behaviour. Every Windows API that normalises paths (most of them, though most allow using the \\?\ prefix to bypass normalisation) will strip trailing dots and spaces from path segments.

    We won't make any change to support it by default, but we do try to make sure that if you use a \\?\ prefix then anything we might do with the path will work. But it's the caller's responsibility to get it right - we don't try to implement POSIX rules around Windows paths.

  2. zooba commented on Feb 6, 2024

    @zooba
    Member

    At its most general, we pass path strings directly to Windows APIs. The best way to get Python to handle these in a particular way is to get Windows to handle them in a particular way.

  3. terryjreedy commented on Feb 6, 2024

    @terryjreedy
    Member

    Should the Using doc Windows section include the pair of sentences quoted above?

  4. zooba commented on Feb 7, 2024

    @zooba
    Member

    Yeah, somewhere we ought to write down our policy on this. I haven't figured out where best to put it though... seems kinda like Dev Guide is the place?

  5. timhoffm commented on Feb 7, 2024

    @timhoffm
    ContributorAuthor

    Yeah, somewhere we ought to write down our policy on this. I haven't figured out where best to put it though... seems kinda like Dev Guide is the place?

    Isn't this rather user-facing? I think adding to https://docs.python.org/3/library/os.path.html somewhere in the vicinity of this note would be helpful:

    grafik

    Something like:

    These modules mimic OS specific behavior (often by calling system APIs) and therefore may differ slightly in behavior across systems. For example, Windows typically trims trailing spaces from file and directory names.

  6. zooba commented on Feb 7, 2024

    @zooba
    Member

    The policy on how we choose what to control vs what to pass through is specific to contributors.

    We should also document somewhere that, at least for Windows, we prefer to expose the OS behaviour "warts and all". I'm not sure exactly how we approach other platforms.

    I also don't like listing specific behaviours, because they vary more often than you'd expect. Your "for example" is fine, but let's not try to make it an exhaustive list.

    It probably also belongs in the os module docs, along with the other platform-specific notes near the top. (It's unfortunate that os.path also accesses the filesystem and so suffers from OS-specific behaviours, but properly the relevant module is os.)

  7. added a commit that references this issue on Jan 28, 2025
  8. added a commit that references this issue on Mar 26, 2025
  9. anton-me commented on Jun 2, 2026

    @anton-me

    There's a real failure mode with the path normalization being inconsistent across os functions.
    As mentioned in the ticket, os.path.isdir() strips the trailing spaces.
    Unfortunately, os.walk() doesn't, it returns an empty iterator by default. It creates a very non-intuitive case when a path to directory is validated as real, but the directory contents are silently skipped.
    While either behavior is understandable in isolation, their inconsistency across a single package comes as user-unfriendly to me. A user may expect the uniform treatment of essentially the same argument. Basically, os lets its abstraction leak.

  10. zooba commented on Jun 2, 2026

    @zooba
    Member

    As mentioned in the discussion, Windows is stripping trailing spaces, not us. It's entirely possible that we are doing something different with os.walk to bypass OS normalisation, but unlikely - could you provide some sample code showing it?

    It's far more likely that a caller is bypassing it themselves by using a \\?\-prefixed path.

  11. anton-me commented on Jun 2, 2026

    @anton-me

    @zooba Sure:

    import os
    import tempfile
    
    with tempfile.TemporaryDirectory() as d:
        p = os.path.join(d, "test")
        os.mkdir(p)
    
        q = p + " "
    
        print("q:", repr(q))
        print("exists:", os.path.exists(q))
        print("isdir:", os.path.isdir(q))
        print("walk:", list(os.walk(q)))
    
        def onerror(e):
            print("walk error:", type(e).__name__, e, "filename:", getattr(e, "filename", None))
    
        print("walk with onerror:")
        print(list(os.walk(q, onerror=onerror)))

    Results:

    q: 'C:\\Users\\am2\\AppData\\Local\\Temp\\tmpi_6cnybm\\test '
    exists: True
    isdir: True
    walk: []
    walk with onerror:
    walk error: FileNotFoundError [WinError 3] The system cannot find the path specified: 'C:\\Users\\am2\\AppData\\Local\\Temp\\tmpi_6cnybm\\test ' filename: C:\Users\am2\AppData\Local\Temp\tmpi_6cnybm\test 
    []
    

    Both os.path.exists() and os.path.isdir() say it's a thing, while os.walk() silently (by default) returns an empty iterator.
    Yes, it is avoidable once you know it, but it would be great if the default behavior would be not burning the users.

  12. zooba commented on Jun 2, 2026

    @zooba
    Member

    Agreed, it looks like FindFirstFileW doesn't normalise individual directory segments, so if you nt._findfirstfile("C:\\dir \\*") then it fails.

    That's ultimately not our fault, and I don't like when we write code to work around the OS design, but it also seems weird that this behaviour could've gone unnoticed for so long?

    Can you confirm your version of Windows? The build number from the System page, ideally - I'm seeing it on my build, which is likely not out in public yet.

    And if anyone else can test nt._findfirstfile(nt.getcwd() + " \\*") and report the result and your OS version (especially if you haven't updated recently), that'd be helpful too.

  13. ChrisDenton commented on Jun 2, 2026

    @ChrisDenton

    The OS trims whitespace from the end of a path. If you tried testing if C:\\dir \\ exists then that would fail too.

    but it also seems weird that this behaviour could've gone unnoticed for so long?

    I would guess it's unusual for people to insert spaces at the end of paths.

  14. zooba commented on Jun 2, 2026

    @zooba
    Member

    I could've sworn it used to trim whitespace from each segment, not just the end, though it does still trim a trailing dot from each part of the path.

    I think our policy of "as close to the OS behaviour as is portable" still applies here, but because we're the ones appending \\* to the path for scandir, trimming spaces first isn't unreasonable. I'd rather use an API that doesn't involve us modifying the path, but if that doesn't exist, then we don't have a lot of choice.

    But since the original issue was opened for "what's the general policy on this" and that's been answered, let's start a new issue specifically for scandir.

  15. anton-me commented on Jun 3, 2026

    @anton-me

    @zooba Re: Windows version, my dev machine runs Win11 Pro 10.0.26200.
    The machine where the issue occurred initially runs Server 2019 1809 (build 17763.8644).

    @ChrisDenton Sometimes, the paths come from e.g. Excel specifications where it's easy for the end users to Ctrl-V some gibberish, trailing spaces are not even the worst; the effort was made to validate the input, but this inconsistency between two functions really worked against my mental model.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-windowstype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions