MAX_PATH and Windows Path/Filename Pitfalls — the 260-Character Limit, Reserved Names, Trailing Dots, and Case Sensitivity
· Go Komura · MAX_PATH, File Path, Long Path, Filename, NTFS, Win32, C#, .NET, Windows Development, Bug Investigation, Technical Consulting
“It only happens on this one user’s machine — ‘file not found’.” “The copy succeeded, but that file won’t open.” It’s not unusual, when investigating a bug in a business app that touches file I/O, to eventually find that the root cause was the length of a path or the filename itself. Users cram case names and dates into folder names, dig deep hierarchies, and casually blow past whatever assumptions you had.
What makes this area tricky is that the limits are split across several layers — the Win32 API’s own limit, the file system’s limit, the shell’s (Explorer’s) limit, and the .NET runtime’s limit — so it’s hard to tell how far you can actually push things and what will still trip you up. This article works through what MAX_PATH=260 actually consists of, the conditions for legally handling longer paths, filename traps like reserved names such as CON and trailing dots, and how case sensitivity is handled — together with the practical C# countermeasures for each.
1. The Bottom Line First
- MAX_PATH=260 is a Win32 API limit that includes the drive letter, colon, backslash, up to 256 characters of path text, and the terminating NUL. The file system layer (NTFS and so on) can handle much longer paths — attach the
\\?\prefix to a Unicode API call and you can specify roughly 32,767 characters in total.12 - Lifting the 260-character limit on Windows 10 version 1607 or later requires both “LongPathsEnabled=1 in the registry” and “longPathAware in the app manifest.” Setting only one has no effect.3
- The .NET (Core)/.NET 5+ runtime doesn’t perform a MAX_PATH check and handles long paths implicitly. Targeting .NET Framework 4.6.2 or later removes the runtime’s own 260-character check.45
- That said, apps that don’t support long paths genuinely remain in the wild. The official documentation itself states plainly that the shell (Explorer) may fail to correctly interpret a path the Win32 API was able to create.1
- Filenames can’t use
< > : " / \ | ? *or control characters (0-31), and CON, PRN, AUX, NUL, COM1-9, and LPT1-9 are treated as reserved names even with an extension attached (e.g. CON.txt).6 - Trailing spaces and dots in a name are silently stripped during path normalization. This is the source of accidents like typing “report-v2.” and getting “report-v2” back, or being unable to access from Windows a file another OS created with a trailing space.67
- Windows filenames are “case-preserving but case-insensitive” by default. NTFS also supports per-directory case sensitivity (
fsutil.exe file setCaseSensitiveInfo), but turning it on comes with the side effect that Windows apps can’t necessarily keep up.89 - On the implementation side, watch out for
Path.Combine’s behavior of discarding earlier arguments when a later argument is rooted (Path.Joinis an alternative on .NET Core), and sanitize user-supplied filenames withPath.GetInvalidFileNameCharsplus your own checks for reserved names and trailing characters.1011
2. What MAX_PATH=260 Actually Is
The maximum path length in the Win32 API is, with a few exceptions, defined as MAX_PATH=260 characters. That 260 has a specific breakdown. A local path is made up of “the drive letter, colon, backslash, the name components separated by backslashes, and the terminating NUL character” — for a D: drive, for example, the maximum is “D:\ + 256 characters of path text + the terminating NUL.”1
In other words, if you think of 260 as “the length available for the filename,” you’re actually four characters short once you account for the drive notation and the terminating NUL. There’s a further, finer-grained restriction: because directory-creation APIs need room to append an 8.3-format filename afterward, a directory path can’t exceed MAX_PATH minus 12 characters.1
The important thing is that this is a limit at the Win32 API layer, not a ceiling imposed by the file system. NTFS supports long filenames and extended-length paths, and the Unicode versions of many Win32 functions accept extended-length paths of roughly 32,767 characters in total. The upper bound on an individual component making up the path (a single folder or file name) is the value returned by GetVolumeInformation, and it’s generally 255 characters.12
This gap — “the API says 260, the file system says roughly 32,767” — is exactly the source of real-world failures. It’s entirely legitimate for a path that one tool could create to be unopenable in another tool (or your own app). Unpacking a deep repository via git clone into a folder with a long name and having the build subsequently break is a textbook example that even the official documentation mentions.1
One more thing worth noting: in older .NET Framework versions, a full path of 260 characters or more would throw System.IO.PathTooLongException. If you see that exception, suspect the path length first.12
3. How to Get Past the 260-Character Wall, and the Conditions for Doing So
There are broadly two ways to handle long paths: the \\?\ prefix, and enabling long paths at the OS level.
3.1. The \\?\ prefix
Attach \\?\ to the front of a path string, and the Win32 API stops parsing the string and passes it straight through to the file system. This gets you past the MAX_PATH limit (a UNC path takes the form \\?\UNC\server\share). There are conditions and side effects, though.16
- It must be a Unicode API (the “W” suffixed functions, or anything called with UTF-16, as .NET does).
- Because normalization is skipped, you can’t use
/as a separator, or relative notation with./... You can’t attach\\?\to a relative path, so relative paths are always limited to MAX_PATH.1 - Not every API supports it — you need to check each API’s reference documentation for support.6
3.2. Enabling long paths on Windows 10 1607 and later — the condition is “both”
On Windows 10 version 1607 and later, you can lift the MAX_PATH limit from many of the commonly used Win32 file and directory functions (CreateFileW, FindFirstFileW, GetFileAttributesW, and so on). This is opt-in for the app, though, and requires both of the following two conditions.3
- The registry value
LongPathsEnabled(REG_DWORD) underHKLM\SYSTEM\CurrentControlSet\Control\FileSystemmust be 1. This can also be set via the group policy “Computer Configuration > Administrative Templates > System > Filesystem > Enable Win32 long paths.” - The application manifest must contain the
longPathAwareelement.
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
# Registry side (requires administrator privileges)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
The classic “I set the registry value but it still doesn’t work” complaint is almost always a missing manifest entry. The official documentation itself hammers this point home: “this registry setting only affects applications that have been modified to make use of the new functionality.” Also, the registry value is cached per-process on the first file-function call and isn’t reloaded for the lifetime of the process, so a reboot may be required to guarantee the setting change is reflected across every app.3
3.3. Where .NET stands on this
- .NET (Core) / .NET 5 and later: the runtime doesn’t perform a MAX_PATH check and handles long paths implicitly. No special code is needed on the app side.4
- .NET Framework: targeting 4.6.2 or later removes the runtime’s 260-character check, and
PathTooLongExceptionis now only thrown when a path exceeds 32,767 characters or when the OS itself returns an error. Even an existing app targeting an earlier version can opt in via the AppContext switchesSwitch.System.IO.BlockLongPaths=false(and, to disable legacy path handling,Switch.System.IO.UseLegacyPathHandling=false).512 - For a .NET Framework app to actually push long paths through in practice, you need both the runtime settings above and the OS-level long-path opt-in plus manifest together. NuGet.exe’s long-path support documentation spells out this exact configuration as a worked example (Windows 10 1607+, a
longPathAwaremanifest, and disablingUseLegacyPathHandling).13
3.4. The reality that “unsupported apps” remain even after all of this
Even after doing all of the above, it doesn’t mean every app out there suddenly handles long paths. The official documentation states plainly that “the shell and the file system have different requirements — the shell UI might not correctly interpret a path that could be created through the Win32 API,”1 and in practice, tools that don’t advertise long-path support remain (for example, NuGet’s own documentation notes that restore in Visual Studio and via msbuild -t:restore doesn’t support long paths13). Even if your own app can create a file at a long path, whether the user can then open it in Explorer or some other tool is a separate question entirely. The decision table in Chapter 6 lays out design choices that account for this asymmetry.
4. Disallowed Characters, Reserved Device Names, and Trailing Dots/Spaces
Alongside path length, the other minefield is the rules around the filename itself. Here’s a rundown of the parts of the official naming rules that business apps most often stumble over.6
| Category | Content | Notes |
|---|---|---|
| Reserved characters | < > : " / \ \| ? * |
Includes the path separator \ (and /) and the drive-letter colon : |
| Control characters | Integer value 0 (NUL) and 1-31 | Not allowed except inside alternate data streams |
| Reserved device names | CON, PRN, AUX, NUL, COM1-COM9, LPT1-LPT9 (plus the superscript-digit forms COM¹-³, LPT¹-³) | Disallowed even with an extension attached (NUL.txt and NUL.tar.gz are equivalent to NUL) |
| Trailing characters | A name ending in a space or a dot | The file system may allow it, but the shell and UI do not |
4.1. Reserved device names — even CON.txt is off-limits
CON and NUL are device names from the MS-DOS era, and they remain reserved in the NT namespace to this day. That’s why a file named “CON” can’t be created through ordinary means, and even attaching an extension, as in CON.txt, still gets interpreted as the reserved name.6 This shows up in the field as things like an accident from trying to save a serial-port log under a name like “COM1.log,” or a folder named “aux” created on Linux that can’t be extracted on Windows.
As an aside, under path normalization rules, a path beginning with a reserved name — “CON” or “COM1.TXT,” say — used to be converted and interpreted as a device path (\\.\CON). Windows 11 changed this interpretation, and now a fully qualified form like \\.\CON is required to actually address the legacy device.7 Even so, since both older OS versions and existing apps still retain the traditional interpretation, the conclusion that reserved names should be avoided for business data doesn’t change.
4.2. Trailing spaces and dots vanish “silently”
The official naming rules state that a file or directory name should not end with a space or a dot.6 Going a step further, Windows path normalization has an explicit rule: “if the path doesn’t end with a separator character, strip all trailing dots and spaces (U+0020).”7
What makes this a genuine problem in practice is that it turns into a different name silently, with no error at all. If a user types “report-v2.” as a name, what actually gets created is a file named “report-v2” — the trailing dot is gone. Conversely, a file like “report “ (with a trailing space) created from the Linux side over SMB can’t be reached through Windows’s normal path notation, because normalization changes the name along the way. The way to get at such a “legal but unreachable through normalization” name is the \\?\ prefix, which skips normalization. The official documentation explicitly calls out this use case, noting that a file like hidden. is inaccessible by any other means.7
Note that a leading dot is perfectly legal — a name like .gitignore can be created without any issue.6
5. Case Is “Preserved but Not Distinguished”
The default behavior of the Windows file system is case-preserving, case-insensitive. Create a file named Readme.txt and its casing is preserved for display, but lookups and comparisons ignore case, so README.TXT reaches that same file. Drive letters are likewise case-insensitive.86
The official naming rules tell app developers not to assume case sensitivity (“OSCAR, Oscar, and oscar should be treated as the same name”), while at the same time noting that NTFS itself supports POSIX-style case sensitivity (though it’s disabled by default).6
This surfaces in practice around Linux interop. Starting with Windows 10 build 17107, you can enable case sensitivity on a per-directory basis.9
# In an administrator PowerShell prompt
fsutil.exe file setCaseSensitiveInfo C:\work\linux-src enable
fsutil.exe file queryCaseSensitiveInfo C:\work\linux-src
This is a valid tool when working with a source tree that originated on Linux (where, say, Makefile and makefile coexist), but the official documentation itself warns of a side effect: a Windows app that assumes the file system is case-insensitive may fail to access files in a directory where case sensitivity has been turned on. Also, you can’t flip the flag unless the target directory is empty, and any newly created subdirectory inherits its parent’s setting.9 Historically, there’s also an officially documented phenomenon where, given two files whose names differ only in case, Explorer would show both, but selecting either one would always open the same single file.9
For business-app design, the practical rule of thumb is: treat “differing only in case” as the same name on Windows by default, but check for case-collision when a filename is going to cross over to Linux. Linux interop has traps around character encoding even before you get to filenames, so it’s worth also checking “An Introduction to Windows Character Encoding — the Mojibake You Hit When Interoperating with Linux.”
6. Practical Guidance for Business Apps — Path Combination, Sanitization, and a Decision Table
6.1. Know Path.Combine’s behavior before you use it
Concatenating paths with + is out of the question, but even Path.Combine has a behavior you need to be aware of. If any argument from the second one onward is a rooted path, every argument before it is discarded entirely.10
var baseDir = @"C:\App\Data";
// If user input happens to be rooted, baseDir gets silently discarded
Path.Combine(baseDir, @"C:\Windows\secret.txt"); // -> "C:\Windows\secret.txt"
Path.Combine(baseDir, @"\evil.txt"); // -> "\evil.txt" (root of the current drive)
Pass a string that came from user input or a config file directly as the second argument, and you get a vulnerability that writes outside the intended save folder. The official documentation itself warns that this behavior can lead to unintended access to sensitive files, and offers Path.Join / Path.TryJoin (not available on .NET Framework) as alternatives.1014 Whichever you use, the standard practice is ultimately to verify that the result, once normalized with Path.GetFullPath, still falls under the base directory.
// Normalize the base side too, then convert to a relative path and check.
// More robust than a string prefix match against quirks like trailing
// separators or a base that's a drive root
var baseFull = Path.GetFullPath(baseDir);
var fullPath = Path.GetFullPath(Path.Combine(baseFull, userInput));
var relative = Path.GetRelativePath(baseFull, fullPath);
if (relative == ".." ||
relative.StartsWith(".." + Path.DirectorySeparatorChar) ||
Path.IsPathRooted(relative)) // an absolute path is returned if it escaped to another drive or a UNC path
{
throw new InvalidOperationException("The save location points outside the expected folder.");
}
Path.GetRelativePath compares paths using the OS’s default convention — that is, on Windows, a case-insensitive comparison, which matches the “Windows defaults to case-insensitive” behavior described in the previous chapter. The flip side of that is: in a location where per-directory case sensitivity has been turned on (see the previous chapter), Data and data can be distinct directories, so a case-insensitive check leaves room to mistakenly treat “a different folder that differs only in case” as if it were inside the base directory. If there’s any chance you’ll be dealing with such a configuration, the safer policy is to refuse to accept a case-sensitivity-enabled location as the base directory in the first place.
One more thing to keep in mind: this check only ever operates on a string that’s been normalized as a path. If a junction or symbolic link exists underneath the base directory, the string can appear to point inside the base while the actual target sits outside it. And because a link can be planted not just at the final file but at an intermediate folder as well (in the shape base\link\file.txt), checking only the endpoint with File.ResolveLinkTarget won’t catch it. The first line of defense is simply avoiding configurations where an untrusted user can create links or junctions under the base directory in the first place. If you genuinely need strict enforcement on top of that, either open the file, obtain the resolved path from the handle (Win32’s GetFinalPathNameByHandle), and verify that it falls under the base directory, or walk each folder component of the path in turn and check whether it’s a link.
6.2. Sanitizing user-supplied filenames
For a feature that assembles a filename from user input — something like “customer name + date.csv” — centralize the sanitization in one place. Path.GetInvalidFileNameChars is the starting point, but the official documentation explicitly states that this array is not guaranteed to be the complete set of invalid characters.11 Reserved device names and trailing dots/spaces aren’t caught by this API, so add your own checks.
private static readonly HashSet<string> ReservedNames =
new(StringComparer.OrdinalIgnoreCase)
{
"CON", "PRN", "AUX", "NUL",
"COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9",
"LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9",
"COM¹","COM²","COM³", // Superscript-digit COM¹-COM³ are also reserved names
"LPT¹","LPT²","LPT³", // Likewise LPT¹-LPT³
};
public static string SanitizeFileName(string input)
{
var invalid = Path.GetInvalidFileNameChars();
var name = new string(input.Select(c => invalid.Contains(c) ? '_' : c).ToArray());
name = name.TrimEnd(' ', '.'); // Remove trailing spaces/dots, since they'd be silently dropped anyway
// Keep within the length limit for a single filename component (usually 255 chars).
// Trim conservatively, leaving room for folder hierarchy and any suffix the app appends later
const int MaxNameLength = 120;
if (name.Length > MaxNameLength)
{
var ext = Path.GetExtension(name);
if (ext.Length > 20)
{
ext = ""; // Don't preserve an absurdly long "extension" as an extension (avoids an exception from a negative range)
}
name = name[..(MaxNameLength - ext.Length)].TrimEnd(' ', '.') + ext;
}
// Always check for empty/reserved names against the "final" result,
// to catch cases where trimming or TrimEnd turns the name into an empty string or a reserved name (like NUL)
var stem = name.Split('.')[0]; // Guards against NUL.txt: check the reserved-name list against the part before the extension
if (name.Length == 0 || ReservedNames.Contains(stem))
{
name = "_" + name; // Adding one character still comfortably fits under the 255-char limit
}
return name;
}
This problem shows up often around CSV output filenames, so for the practicalities of CSV itself, see also “CSV Isn’t Just ‘Plain Text’ — Practical CSV Handling in C# Business Apps.”
6.3. The traps of relative paths and the current directory
Relative paths have two traps. First, the current directory is a per-process setting, so it can be changed at any time from any thread. The official documentation goes as far as to say “relative paths are dangerous in multithreaded applications,” and on .NET Core 2.1 and later you can use Path.GetFullPath(string, string), which lets you specify the base path explicitly.7 Second, a form like C:tmp.txt — with no backslash right after the drive letter — is “a path relative to the current directory on the C: drive,” not an absolute path. This “drive-relative path” is explicitly called out by the official documentation as a classic source of bugs in programs and scripts.7
Make it a habit that any path received from a config file or user input gets converted to an absolute path via Path.GetFullPath at the point you receive it, before you log it or validate it.
6.4. Decision table — should you support long paths, or reject them at the door?
| Situation | Recommendation | Reason |
|---|---|---|
| A general business app where users freely choose the save location | Validate at the door and reject (check full path length and the filename before saving, and surface a clear error) | Even if your own app supports it, there’s still a risk that Explorer or a downstream tool won’t be able to open it1 |
| Backing up, syncing, or extracting archives — i.e. “reading” a deep hierarchy someone else created | Support long paths (.NET Core-family + manifest if needed; on Framework, 4.6.2+ configuration) | You have no control over the input, and business grinds to a halt if you can’t read it54 |
| Your own app is the one “creating” a deep hierarchy | As a rule, redesign it so it doesn’t (flatten the hierarchy, adopt hashed names, and so on) | There’s a good chance whoever ends up using the path you created — a person or another app — won’t support it1 |
| Exchanging files with Linux/WSL | Check reserved names, case collisions, and trailing characters before transfer | Otherwise you end up with files that are unreachable on the Windows side69 |
| Generating a filename from user input | Consolidate sanitization into a shared function combining GetInvalidFileNameChars with reserved-name and trailing-character checks |
The API’s own array alone is incomplete11 |
7. Troubleshooting — “It’s Visible in Explorer, but I Can’t Open It”
Here’s a step-by-step approach for the classic complaint: “the file shows up fine in Explorer, but opening it in the app gives ‘file not found.’”
| Check | Method | If it matches |
|---|---|---|
| Is the full path close to 260 characters? | In PowerShell: (Get-ChildItem -Recurse).FullName \| Where-Object { $_.Length -ge 250 } |
Shorten a parent folder name, or consider long-path support (Chapter 3) |
| Is the filename a reserved name (aux, con, com1, etc.)? | Visually inspect the name; this includes names with an extension attached6 | Rename it (if the source is Linux etc., convert it during transfer) |
| Does it end with a space or dot? | Check with cmd /c dir /x or a quoted display |
Remove or rename it via a path with the \\?\ prefix7 |
| Are there two files with the same name differing only in case? | Common in folders that originated from WSL/Git9 | Rename one, or reconsider what that directory is used for |
| Are you using a relative path or a drive-relative path? | Log the absolute path you actually attempted to open | Convert to an absolute path with Path.GetFullPath before use7 |
The first thing worth recommending for an investigation is logging the exact path the app attempted to open, as an absolute path, wrapped in quotes, in the app’s error log. An exception message that just says “file not found” leaves you unable to later tell whether the path was truncated, renamed during normalization, or was simply pointing at a different directory the whole time. Wrap it in quotes when you log it, and a hard-to-spot problem like a trailing space becomes obvious at a glance.
One more note: a failed DLL load is also a regular member of the “file not found”-style error family, but that’s more often a search-order problem than a path-length one — we cover that in “How Windows DLL Name Resolution Works — Search Order and SxS.”
8. Summary
- MAX_PATH=260 is a Win32 API limit covering “
D:\+ up to 256 characters + the terminating NUL,” while NTFS itself can handle extended-length paths of roughly 32,767 characters. Directories carry a further restriction of MAX_PATH minus 12. - Getting past 260 characters requires either the
\\?\prefix (Unicode API only, no relative paths) or enabling long paths on Windows 10 1607 or later (both the registry’sLongPathsEnabledand the manifest’slongPathAware). - .NET (Core)/5+ handles long paths implicitly, and targeting .NET Framework 4.6.2 or later removes the runtime check. Apps that don’t support it, including Explorer, still exist, though, so treat “can it be created” and “can the user handle it” as separate questions.
- Filenames can’t contain the reserved characters (
< > : " / \ | ? *) or control characters; reserved device names like CON, NUL, and COM1 are off-limits even with an extension attached; and trailing spaces/dots are silently stripped during normalization. - Case is “preserved but not distinguished” by default. Per-directory case sensitivity via
fsutil file setCaseSensitiveInfois useful for WSL interop, but it comes at the cost of a risk that Windows apps will misbehave. - On the implementation side, the standard practices are: validating the base directory with
Path.Combine’s rooted-argument behavior in mind, consolidating sanitization aroundGetInvalidFileNameCharsplus reserved-name/trailing-character checks, and eliminating relative paths by converting to absolute paths withPath.GetFullPath.
Related Articles
- CSV Isn’t Just “Plain Text” — Practical CSV Handling in C# Business Apps (Character Encoding, Excel Compatibility, Injection Countermeasures)
- An Introduction to Windows Character Encoding — the Mojibake You Hit When Interoperating with Linux
- Windows Character Encoding and Line Endings — the Basics of Mojibake and CRLF/LF
- How Windows DLL Name Resolution Works — Search Order and SxS
Related Consulting Areas
Komura Software LLC handles root-cause investigation of file-I/O bugs like “only this specific environment or file won’t open,” reviewing long-path support and filename validation design in existing business apps, and design consulting for file interoperability in mixed Windows/Linux environments.
- Windows App Development
- Bug Investigation & Root Cause Analysis
- Technical Consulting & Design Review
- Contact
References
-
Microsoft Learn, Maximum Path Length Limitation. On the definition of MAX_PATH=260 and its “drive letter + colon + backslash + 256 characters + terminating NUL” breakdown, the roughly 32,767-character extended-length path available through Unicode APIs and the
\\?\prefix, the component-length limit (generally 255 characters), relative paths always being limited to MAX_PATH, directory creation being capped at MAX_PATH minus 12, and the shell and file system having different requirements such that the shell UI may not interpret a path createable via Win32. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 -
Microsoft Learn, NTFS overview. On NTFS supporting long filenames and roughly 32,767-character extended-length paths, and backward compatibility via 8.3 aliases. ↩ ↩2
-
Microsoft Learn, Maximum Path Length Limitation — Enable long paths in Windows 10, version 1607, and later. On needing both the registry value LongPathsEnabled=1 and the app manifest’s longPathAware element on Windows 10 1607 and later, the group policy setting, the registry value being cached per-process, and the list of Win32 functions the limit is lifted for. ↩ ↩2 ↩3
-
Microsoft Learn, File path formats on Windows systems — Skip normalization. On .NET Core and .NET 5+ implicitly handling long paths without a MAX_PATH check (the MAX_PATH check applies only to .NET Framework), and on
\\?\being the mechanism that skips normalization. ↩ ↩2 ↩3 -
Microsoft Learn, Retargeting changes for migration to .NET Framework 4.6.x. On targeting .NET Framework 4.6.2 supporting long paths (up to 32K characters) and removing the 260-character limit, and existing apps targeting an earlier version being able to opt in via Switch.System.IO.BlockLongPaths=false. ↩ ↩2 ↩3
-
Microsoft Learn, Naming Files, Paths, and Namespaces. On reserved characters (
< > : " / \ | ? *) and control characters (0-31), reserved device names (CON/PRN/AUX/NUL/COM1-9/LPT1-9 and their superscript-digit forms), names like NUL.txt with an extension being equivalent to the reserved name, names not ending in a trailing space or dot, a leading dot being legal, not assuming case sensitivity plus NTFS’s POSIX semantics, and the behavior and Unicode API requirement of the\\?\prefix. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 -
Microsoft Learn, File path formats on Windows systems — Path normalization. On path normalization stripping trailing dots and spaces, a name like
hidden.being accessible only via\\?\, the interpretation of legacy device names like CON and the change in Windows 11, drive-relative paths (C:tmp.txt) being a common source of bugs, the current directory being per-process and relative paths being dangerous under multithreading, and Path.GetFullPath(String, String). ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Microsoft Learn, File path formats on Windows systems — Case and the Windows file system. On directory and file names preserving the case used at creation time while name comparisons remain case-insensitive. ↩ ↩2
-
Microsoft Learn, Adjust case sensitivity. On per-directory case sensitivity (fsutil.exe file setCaseSensitiveInfo) available from Windows 10 build 17107 on, the requirement of administrator privileges and an empty directory to change it, new subdirectories inheriting the setting, the warning that Windows apps assuming case-insensitivity may misbehave, and the historical case where two files differing only in case both appeared in Explorer but only one could ever actually be opened. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Path.Combine Method. On a rooted path in any argument after the first causing all preceding path elements to be discarded and a string starting from the rooted element to be returned, this potentially leading to unintended access to sensitive files, and Join/TryJoin (unavailable on .NET Framework) being offered as an alternative. ↩ ↩2 ↩3
-
Microsoft Learn, Path.GetInvalidFileNameChars Method. On this returning an array of characters not allowed in a filename, and the returned array not being guaranteed to be the complete set of invalid characters, which can vary by file system. ↩ ↩2 ↩3
-
Microsoft Learn, PathTooLongException Class. On this being the exception thrown when a path exceeds the system-defined maximum length, and on .NET Framework 4.6.2 and later only throwing it when the path exceeds 32,767 characters or the OS itself returns an error. ↩ ↩2
-
Microsoft Learn, Long Path Support (NuGet CLI). On the actual configuration required for .NET Framework-based tools to use long paths (Windows 10 1607+ or 1511 + .NET Framework 4.6.2, the Win32 long paths policy, the longPathAware manifest plus disabling UseLegacyPathHandling), and on restore in Visual Studio and msbuild not supporting long paths. ↩ ↩2
-
Microsoft Learn, Path.Join Method. On Join concatenating a rooted subsequent path rather than discarding it, with worked examples of the behavioral difference from Combine. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Sleep, Hibernation, Modern Standby, and Long-Running Apps — Designing Around 'It Stopped Overnight'
Why a long-running Windows app can end up 'stopped by the time you check it in the morning,' worked through from the differences between ...
Pitfalls of Network Drives and UNC Paths — Working With File Servers (Shared Folders) From a Business Application
This article organizes the classic problems that show up when a business application writes to or watches a shared folder: why a drive le...
Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide (DllImport / LibraryImport / CsWin32)
A practical rundown of what to watch for when calling Win32 APIs and native DLLs from C# via P/Invoke. Covers the differences between Dll...
When Your In-House Windows App Gets Flagged as a Virus — Handling Microsoft Defender False Positives and Living With the Performance Impact
We lay out the proper way to respond when Microsoft Defender flags your in-house Windows app as malware: how modern antivirus detection a...
Do Business Apps Run on Windows on Arm? — The Reality of x64 Emulation (Prism) and Native DLLs/COM
An answer, aimed at developers and IT staff, to 'will our business app run on Windows on Arm?' Covers how x64 emulation (Prism) works, th...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Bug Investigation & Long-Run Failures
Topic page for intermittent failures, communication diagnosis, long-run crashes, and failure-path test foundations.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Bug Investigation & Root Cause Analysis
We investigate difficult production issues such as intermittent failures, long-run crashes, leaks, and communication stoppages.
Frequently Asked Questions
Common questions about the topic of this article.
- How many characters can a Windows path be?
- By default, the Win32 API limit is MAX_PATH=260 characters, and that length includes the drive letter, colon, backslash, up to 256 characters of path text, and the terminating NUL. The file system itself (NTFS and others) can handle much longer paths — pass a path with the \\?\ prefix to a Unicode API and you can specify roughly 32,767 characters in total. That said, a single folder or file name (a path component) is generally capped at 255 characters, and relative paths are always limited to MAX_PATH.
- How do I lift the MAX_PATH 260-character limit?
- On Windows 10 version 1607 and later, setting both the registry value LongPathsEnabled=1 (or the group policy 'Enable Win32 long paths') and the longPathAware element in the application manifest removes the 260-character limit for many Win32 file functions. Setting only one of the two has no effect. The .NET (Core)/.NET 5+ runtime doesn't perform a MAX_PATH check at all and handles long paths implicitly, and targeting .NET Framework 4.6.2 or later removes the runtime's own 260-character check. However, apps that don't support long paths — including Explorer itself — still exist, so you need to consider who will end up handling the long path you create.
- Why can't I create a file named CON or NUL?
- CON, PRN, AUX, NUL, COM1 through COM9, and LPT1 through LPT9 are reserved device names dating back to the MS-DOS era, and Windows interprets these names as devices rather than files. Adding an extension, as in NUL.txt, doesn't help — it's still treated the same as NUL. Windows 11 changed part of how these paths are interpreted, but since older OS versions and a large number of existing apps still follow the traditional interpretation, it remains safest to avoid these names for business data files.
- Does Windows treat filenames as case-sensitive?
- By default, Windows is case-preserving but case-insensitive. Create a file named Readme.txt and its case is preserved for display, but trying to open README.TXT still reaches the same file. NTFS also supports POSIX-style case sensitivity, and starting with Windows 10 build 17107 you can enable per-directory case sensitivity with fsutil.exe file setCaseSensitiveInfo — but doing so has the side effect of breaking Windows apps that assume case-insensitivity, so it should be limited to situations where it's genuinely needed, such as WSL interop.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links