Why does Path.Combine not properly concatenate filenames that start with a backslash Path.DirectorySeparatorChar?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Path.Combine in .NET discards all preceding path segments when any subsequent argument is an absolute path (starts with \, /, or a drive letter like C:\). This is by design, not a bug — it follows the POSIX and Windows convention that an absolute path component overrides everything before it. The fix is to strip the leading separator from the second argument before combining, or use Path.Join (.NET Core 3.0+) which always concatenates without discarding segments.
The Problem
The second argument \Documents\file.txt starts with \, which .NET treats as a root-relative path. Path.Combine discards C:\Users\Alice and returns only the second argument.
How Path.Combine Decides
Path.Combine processes arguments left to right. For each argument, it checks:
- Is this argument a rooted/absolute path?
- If yes, discard everything accumulated so far and start fresh from this argument.
- If no, append it to the current result with a separator.
Fix 1: Strip the Leading Separator
Helper Method
Fix 2: Use Path.Join (.NET Core 3.0+)
Path.Join always concatenates without discarding segments:
Path.Join is the recommended replacement for Path.Combine when you always want concatenation behavior.
Comparison: Path.Combine vs Path.Join
| Method | Leading `` in arg | Drive letter in arg | Available since | |
Path.Combine | Discards previous segments | Discards previous segments | .NET 1.0 | |
Path.Join | Concatenates as-is | Concatenates as-is | .NET Core 3.0 |
Real-World Scenario: User-Supplied Paths
Checking If a Path Is Rooted
Use Path.IsPathRooted to check whether a segment will cause Path.Combine to discard previous segments.
Common Pitfalls
- Assuming
Path.Combinealways concatenates: It does not. Any rooted argument (starting with\,/, or a drive letter) causes all previous segments to be discarded. Verify inputs or usePath.Join. - Not validating user-supplied paths: User paths like
\..\..\etc\passwdcombined withPath.Combinecan escape the intended directory. Always validate that the resolved full path starts with the expected base directory. - Using
Path.Joinon .NET Framework:Path.Joinis only available in .NET Core 3.0+ and .NET 5+. On .NET Framework, use theTrimStartapproach or theSafeCombinehelper. - Hardcoding
\\instead of usingPath.DirectorySeparatorChar: On Linux/macOS running .NET, the separator is/, not\. UsePath.DirectorySeparatorCharorPath.Combine/Path.Joinfor cross-platform compatibility. - Double separators:
Path.Combine("C:\\folder\\", "file.txt")producesC:\folder\file.txt(no double backslash). But manual string concatenation with+can produceC:\folder\\file.txt. Always usePath.CombineorPath.Joininstead of string concatenation for paths.
Summary
Path.Combinediscards all preceding segments when an argument starts with\,/, or a drive letter- This is by design — rooted paths are treated as absolute references that override the base
- Strip leading separators with
TrimStart(Path.DirectorySeparatorChar)before combining - Use
Path.Join(.NET Core 3.0+) for always-concatenate behavior - Always validate combined paths against path traversal attacks when dealing with user input

