JDK-8355342 : File.getCanonicalPath on Java 24 resolves paths on network drives to UNC format
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 24,25
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows
  • CPU: generic
  • Submitted: 2025-04-22
  • Updated: 2025-11-19
  • Resolved: 2025-11-06
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 26
26 b24Fixed
Related Reports
Causes :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
OS: Windows 11 Pro 
Locale: Japanese
JDK: OpenJDK 24.0.1 and 24

A DESCRIPTION OF THE PROBLEM :
This method is implementation-dependent, so this may not strictly qualify as a bug. However, I'm reporting it because it seems to be an unintended behavioral change.

Starting with Java 24, on Windows, when passing the path of a file mounted on a network drive (e.g., Z:\a.txt) to File.getCanonicalPath, the path is now automatically resolved to its UNC form (e.g., \\192.168.1.3\...).

UNC paths cannot be used as working directories when launching external processes on Windows (e.g., via Runtime.exec). As a result, this change breaks some existing code that worked correctly in previous Java versions.

While this behavior can be avoided by using the more modern Path.toRealPath instead of File.getCanonicalPath -- which does not expand to UNC paths -- it cannot be avoided in APIs that likely rely internally on File.getCanonicalPath, such as JFileChooser.setDirectory in Swing.

Overall, I believe it would be better for compatibility with existing code if this behavioral change in File.getCanonicalPath were reverted or adjusted.

REGRESSION : Last worked in version 23

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. On Windows, mount a network location as a network drive (e.g., Z:\).

2. Create a File instance pointing to a file or folder on the network drive (e.g., Z:\a.txt).

3-A. Call getCanonicalPath() on the instance and observe the resulting path.

3-B. Alternatively, pass the network drive path to JFileChooser#setDirectory, select a file within the directory, and observe the returned path.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The path on the network drive is returned (e.g., Z:\a.txt).
ACTUAL -
The UNC path is returned instead (e.g., \\192.168.1.3\share-directory\a.txt).

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.IOException;

public class ReportExample {

    public static void main(String[] args) throws IOException {

        // When the network drive "Z:" is mounted,
        // and "a.txt" exists on that drive,
        // create a File instance pointing to it.
        java.io.File file = new File("Z:\\a.txt");

        // Use the target method to retrieve the path.
        String path = file.getCanonicalPath();

        // Print the result.
        System.out.println("Retrived path: " + path);

        // Java 24 (if a.txt exists): \\192.168.1.3\share-directory\a.txt
        // Java 24 (if a.txt does NOT exist): Z:\a.txt
        // Java 23: Z:\a.txt
    }
}
---------- END SOURCE ----------


Comments
Changeset: 1f08a3ed Branch: master Author: Brian Burkhalter <bpb@openjdk.org> Date: 2025-11-06 16:01:37 +0000 URL: https://git.openjdk.org/jdk/commit/1f08a3ede2445fb05d9700a1293d681ca89cbf5b
06-11-2025

Given the files C:\Temp\dir C:\Temp\file.txt C:\Temp\link [-> file.txt] then if Z: is mapped to C:\Temp, GetFinalPathNameByHandleW applied to Z:\dir\..\link gives the following results: VOLUME_NAME_DOS (0) \\?\UNC\localhost\c$\Temp\file.txt VOLUME_NAME_GUID (1) ERROR_PATH_NOT_FOUND VOLUME_NAME_NONE (4) \localhost\c$\Temp\file.txt VOLUME_NAME_NT (2) \Device\Mup\localhost\c$\Temp\file.txt Setting the FILE_NAME_OPENED flag makes no difference. In the getFinalPath function in WinNTFileSystem_md.c, the flags are zero which is equivalent to FILE_NAME_NORMALIZED | VOLUME_NAME_DOS. This function strips the UNC prefix which explains the result.
17-10-2025

Given Directory of C:\Temp 09/19/2025 01:09 PM <DIR> . 09/10/2025 04:41 PM 18 file.txt 09/19/2025 01:09 PM <SYMLINK> link [file.txt] and Z: is mapped to \\localhost\c$\Temp, then jshell> Path.of("Z:\\link").toRealPath() $1 ==> \\localhost\c$\Temp\file.txt so Path.toRealPath has the same problem as File.getCanonicalPath for this configuration. However if Z: is mapped to a network location on another machine which contains the same files, the Path.toRealPath() fails as | Exception java.nio.file.FileSystemException: Z:\link: The name of the file cannot be resolved by the system | at WindowsException.translateToIOException (WindowsException.java:92) | at WindowsException.rethrowAsIOException (WindowsException.java:103) | at WindowsException.rethrowAsIOException (WindowsException.java:108) | at WindowsLinkSupport.getFinalPath (WindowsLinkSupport.java:104) | at WindowsLinkSupport.getRealPath (WindowsLinkSupport.java:271) | at WindowsPath.toRealPath (WindowsPath.java:944) | at WindowsPath.toRealPath (WindowsPath.java:42) but getCanonicalPath returns jshell> new File("Z:\\link").getCanonicalPath() $2 ==> "Z:\\link"
19-09-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/27324 Date: 2025-09-16 21:13:13 +0000
16-09-2025

In java.io.WinNTFileSystem.canonicalize(), the result of canonicalize0() is passed to GetFinalPathNameByHandleW() with flags value zero which is equivalent to FILE_NAME_NORMALIZED|VOLUME_NAME_DOS. If the canonicalized path starts with the letter of a mapped drive, this function converts the drive letter to "\\?\", i.e., "\\server\share\..." syntax. This use of GetFinalPathNameByHandleW was added by the change made for JDK-8003887 in JDK 24 build 15, hence the behavioral difference with respect to JDK 23.
11-09-2025

I mounted the location \\localhost\c$\Temp on drive letter Z:. For the existing file Z:\junk.txt for the Path jshell> Path p = Path.of("Z:\\junk.txt") p ==> Z:\junk.txt JDK 23 gives jshell> p.toFile().getCanonicalPath() $2 ==> "Z:\\junk.txt" whereas JDK 24 and 26-internal give $2 ==> "\\\\localhost\\c$\\Temp\\junk.txt" For toRealPath they all give jshell> p.toRealPath() $3 ==> Z:\junk.txt
11-09-2025

This is a likely side effect of the change made for JDK-8003887.
25-06-2025

I think we need to get the output of Path::toRealPath on Z:\test.zip to see what it produces.
24-04-2025

Impact -> H (Regression) Likelihood -> L (Probably not an issue) Workaround -> M (Somewhere in-between the extremes) Priority -> P3
23-04-2025

The observations on Windows 11: JDK 24+14: Passed, the output is Z:\test.zip, the same as previous JDKs. JDK 24+15: Failed, the output becomes \\wsl$\Ubuntu-20.04\test.zip JDK 25ea+6: Failed.
23-04-2025