How to Resolve java.nio.file.InvalidPathException: Illegal char <:> When Copying Classpath Resources Between Packages in Java
Java developers frequently work with classpath resources—such as configuration files, templates, or static assets—often needing to copy these resources between packages or directories during application execution. However, this seemingly straightforward task can sometimes trigger the java.nio.file.InvalidPathException: Illegal char <:> error. This exception is typically caused by mishandling of file paths, especially when dealing with URLs, resource locations, or cross-platform file system differences.
In this blog, we’ll demystify this error, explore its root causes, and provide step-by-step solutions to resolve it. Whether you’re copying resources from a JAR file or the local file system, you’ll learn best practices to avoid path-related issues and ensure smooth resource handling.
Table of Contents#
- Understanding the
InvalidPathException - Common Scenarios Triggering the Exception
- Root Causes of the "Illegal char <:>" Error
- Step-by-Step Solutions
- Best Practices for Classpath Resource Handling
- Conclusion
- References
Understanding the InvalidPathException#
The java.nio.file.InvalidPathException is thrown when an invalid character is detected in a file path. The error message Illegal char <:> specifically indicates that a colon (:) was found in a context where it is not allowed.
In Java, file paths are validated against the underlying operating system’s rules. For example:
- On Windows, colons are reserved for drive letters (e.g.,
C:) and cannot appear in file or directory names. - On Unix-based systems (Linux/macOS), colons are allowed in filenames but are rarely used, as they’re reserved for separating paths in environment variables (e.g.,
PATH=/usr/bin:/bin).
When working with classpath resources, the colon often sneaks into paths via mishandled URLs or incorrect path parsing, leading to this exception.
Common Scenarios Triggering the Exception#
The Illegal char <:> error typically arises in two main scenarios when copying classpath resources:
Scenario 1: Resources Packaged in JAR Files#
Most Java applications package resources into JAR files. When accessing resources inside a JAR, the JVM returns a URL with the jar protocol (e.g., jar:file:/C:/app/myapp.jar!//com/example/resources/config.txt). If you naively extract the "path" from this URL (e.g., using URL.getPath()), you might end up with a string containing jar:file:/..., which includes colons. Passing this string directly to Paths.get() or new File() will trigger the exception.
Scenario 2: File-System Resources with Mishandled URLs#
Even for resources stored directly on the file system (not in a JAR), URLs returned by ClassLoader.getResource() use the file protocol (e.g., file:/C:/projects/myapp/src/main/resources/com/example/config.txt). Using URL.getPath() here returns a string like /C:/projects/..., which includes a colon after the drive letter. On Windows, Paths.get("/C:/...") is invalid because of the leading slash before the drive letter, causing the colon to be misinterpreted.
Root Causes of the "Illegal char <:>" Error#
To resolve the error, we must first understand why the colon ends up in the path. Here are the key culprits:
1. Incorrect URL-to-Path Conversion#
Using URL.getPath() to extract the resource path is error-prone. For file protocol URLs (e.g., file:/C:/...), URL.getPath() returns a string with a leading slash (e.g., /C:/projects/...). On Windows, Paths.get("/C:/...") fails because the leading slash is unnecessary and invalidates the drive letter syntax (C:).
2. Treating JAR Resources as File System Files#
Resources inside JAR files are not actual files on the disk. Attempting to convert their URLs (e.g., jar:file:/...) to File or Path objects is invalid, as they exist only within the JAR archive. This leads to forced path parsing that retains the jar: protocol’s colon.
3. Using OS-Specific File Separators in Classpath Paths#
Classpath resources are always accessed using forward slashes (/) as separators, regardless of the OS. Using backslashes (\) (common on Windows) in resource paths (e.g., com\example\config.txt) causes misparsing and may introduce invalid characters.
Step-by-Step Solutions#
Let’s resolve the InvalidPathException with actionable, code-driven solutions.
Solution 1: Use Streams Instead of File Paths for JAR Resources#
Resources in JAR files cannot be accessed as File objects. Instead, use input streams to read the resource and write it to the target location. This avoids path conversion entirely.
Problematic Code (Causes Exception):#
// Attempting to copy a JAR resource by converting URL to Path (fails)
URL resourceUrl = getClass().getClassLoader().getResource("com/example/source/resource.txt");
Path sourcePath = Paths.get(resourceUrl.getPath()); // Error: colon in "jar:file:/..." path
Path targetPath = Paths.get("com/example/target/resource.txt");
Files.copy(sourcePath, targetPath); // Throws InvalidPathExceptionFixed Code (Uses Streams):#
// Copy resource using input/output streams (works for JAR and file-system resources)
try (InputStream in = getClass().getClassLoader().getResourceAsStream("com/example/source/resource.txt")) {
if (in == null) {
throw new IOException("Resource not found: com/example/source/resource.txt");
}
Path targetPath = Paths.get("com/example/target/resource.txt");
// Ensure target directory exists
Files.createDirectories(targetPath.getParent());
// Copy stream to target path
Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}Why it works: Streams read the resource directly from the JAR or file system without needing a Path or File object, avoiding colon-related path errors.
Solution 2: Correctly Handle File-System Resources with URL.toURI()#
For resources on the file system (not in a JAR), use URL.toURI() instead of URL.getPath() to safely convert the URL to a Path. URL.toURI() properly parses the URL, including handling drive letters on Windows.
Problematic Code (Incorrect Path Conversion):#
URL resourceUrl = getClass().getClassLoader().getResource("com/example/source/resource.txt");
String path = resourceUrl.getPath(); // Returns "/C:/projects/.../resource.txt" (invalid on Windows)
Path sourcePath = Paths.get(path); // Throws InvalidPathException: Illegal char <:>Fixed Code (Using URL.toURI()):#
URL resourceUrl = getClass().getClassLoader().getResource("com/example/source/resource.txt");
if (resourceUrl == null) {
throw new IOException("Resource not found");
}
// Use toURI() to correctly parse the URL (handles Windows drive letters)
URI resourceUri = resourceUrl.toURI();
Path sourcePath = Paths.get(resourceUri); // Valid path
Path targetPath = Paths.get("com/example/target/resource.txt");
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);Why it works: URL.toURI() converts the URL into a well-formed URI, which Paths.get() can parse correctly (e.g., file:/C:/... becomes C:\... on Windows, with the colon properly recognized as part of the drive letter).
Solution 3: Validate Resource Location Before Path Conversion#
Check if the resource is in a JAR or on the file system using the URL protocol. For JAR resources, use streams; for file-system resources, use toURI().
Example Code:#
URL resourceUrl = getClass().getClassLoader().getResource("com/example/source/resource.txt");
if (resourceUrl == null) {
throw new IOException("Resource not found");
}
Path sourcePath;
if ("file".equals(resourceUrl.getProtocol())) {
// File-system resource: use toURI()
sourcePath = Paths.get(resourceUrl.toURI());
} else if ("jar".equals(resourceUrl.getProtocol())) {
// JAR resource: use streams (see Solution 1)
try (InputStream in = resourceUrl.openStream()) {
Path targetPath = Paths.get("com/example/target/resource.txt");
Files.createDirectories(targetPath.getParent());
Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
return; // Exit after handling JAR case
}
} else {
throw new IOException("Unsupported protocol: " + resourceUrl.getProtocol());
}
// Copy file-system resource
Path targetPath = Paths.get("com/example/target/resource.txt");
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);Best Practices for Classpath Resource Handling#
To avoid InvalidPathException and other resource-related issues, follow these best practices:
1. Always Use Forward Slashes in Resource Paths#
Classpath resources are accessed using forward slashes (/), even on Windows. Never use backslashes (\), as they are invalid in classpath paths.
Correct: com/example/resource.txt
Incorrect: com\example\resource.txt
2. Prefer getResourceAsStream() Over getResource()#
ClassLoader.getResourceAsStream() directly returns an input stream, avoiding URL parsing entirely. Use this for most resource-reading tasks.
3. Avoid File for JAR Resources#
Resources in JARs are not files. Using new File(url.getPath()) will fail. Always use streams for JAR resources.
4. Handle Encoded Characters in URLs#
URLs may contain encoded characters (e.g., spaces as %20). URL.toURI() automatically decodes these, while URL.getPath() does not. Use toURI() for file-system resources to avoid encoding issues.
5. Test Across Operating Systems#
Path handling differs between Windows, Linux, and macOS. Test resource copying on all target OSes to catch platform-specific issues.
Conclusion#
The java.nio.file.InvalidPathException: Illegal char <:> error occurs when invalid characters (like colons) are introduced into file paths, typically due to mishandling URLs for classpath resources. By avoiding URL-to-path conversion for JAR resources, using streams, and correctly parsing URLs with toURI(), you can resolve this error.
Remember: streams are the safest way to handle resources, especially those inside JAR files. For file-system resources, use URL.toURI() to ensure proper path parsing across operating systems.