apple/pkl · error · IOException
Unsupported protocol:
Error message
Unsupported protocol:
What it means
IoUtils.toUrl converts a URI to a java.net.URL; on GraalVM native-image, URI schemes without a registered protocol handler throw UnsupportedFeatureError at runtime. toUrl catches that specific error and rethrows an IOException "Unsupported protocol: <scheme>", leaving context-giving to the caller, since the scheme has no URL stream handler available in the native binary.
Solutions
- Use a scheme with a registered handler (file:, http:, https:) for the actual fetch
- Route custom-scheme URIs through their proper reader/resolver instead of toUrl/URL.openStream
- Check the scheme spelling in the URI
- If embedding Pkl native-image, register a protocol handler for the custom scheme
Example fix
// before
URL url = IoUtils.toUrl(URI.create("projectpartial:foo"));
// after
URL url = IoUtils.toUrl(URI.create("file:///path/to/project/foo")); Defensive patterns
Strategy: try-catch
Validate before calling
const SUPPORTED = ['http', 'https', 'file', 'jar']; const hasSupportedProtocol = (uri) => SUPPORTED.includes((uri.match(/^([a-z][a-z0-9+.-]*):/) || [])[1] || '');
Type guard
const isSupportedUrl = (uri) => /^(https?|file|jar):/.test(uri);
Try / catch
try {
URL url = IoUtils.toUrl(uri);
} catch (IOException e) {
if (e.getMessage().startsWith("Unsupported protocol:")) {
// resolve via the scheme-specific reader instead of URL.openStream
} else throw e;
} Prevention
- Only call toUrl with file/http/https/jar URIs
- Route custom-scheme URIs through their dedicated reader/resolver
- If on GraalVM native-image, remember protocol handlers beyond the defaults are not registered
- Validate scheme spelling before conversion
When it happens
Trigger: Calling IoUtils.toUrl with a URI whose scheme has no java.net.URL protocol handler in the current runtime — e.g. a custom scheme ("project:", "module:", "pkg:") on GraalVM native-image, or genuinely unhandled schemes.
Common situations: Running Pkl as a native image where only file/http(s)/jar handlers are registered but code attempts to open a custom-scheme URI as a URL; typo'd schemes; or code bypassing the proper scheme-specific reader.
Related errors
- Cannot generate documentation for just one module within a…
- Cannot receive request messages before transport start.
- Cannot resolve relative URI
- cannotAnalyzeRelativeModuleUri
- cannotGlobUri
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/1d737b1671dbab96.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-core/src/main/java/org/pkl/core/util/IoUtils.java:97
"upgrade",
"te",
"transfer-encoding",
"trailer"
};
// keep in sync with stdlib `EvaluatorSettings.reservedHttpHeaderPrefix`
private static final String[] reservedHeaderPrefixes = {"proxy-", "sec-"};
private IoUtils() {}
public static URL toUrl(URI uri) throws IOException {
try {
return uri.toURL();
} catch (Error e) {
// best we can do for now
// rely on caller to provide context, e.g., the requested module URI
if (e.getClass().getName().equals("com.oracle.svm.core.jdk.UnsupportedFeatureError")) {
throw new IOException("Unsupported protocol: " + uri.getScheme());
}
throw e;
}
}
/** Checks whether the given string is "URI-like", i.e. matches a pattern like {@code foo:bar}. */
public static boolean isUriLike(String str) {
return uriLike.matcher(str).matches();
}
public static boolean isWindowsAbsolutePath(String str) {
return windowsDriveLetterLike.matcher(str).matches();
}
/**
* Converts the given string to a {@link URI}. This method MUST be used for constructing module
* and resource URIs. Unlike {@code new URI(str)}, it correctly escapes paths of relative URIs.View on GitHub (pinned to f3efcbfc9b)