apache/cordova-android · error · FileNotFoundException
Plugin can't handle uri: ${uri}
Error message
Plugin can't handle uri: ${uri} What it means
CordovaPlugin.handleOpenForRead is the default implementation for cdvplugin://pluginId/... URLs and it always throws FileNotFoundException. CordovaResourceApi routes plugin-scheme reads here; if your plugin does not override this method, any attempt to open one of its plugin:// URIs fails — the base class 'can't handle' the URI by design.
Source
Thrown at framework/src/org/apache/cordova/CordovaPlugin.java:332
* public Uri remapUri(Uri uri) { return toPluginUri(uri); }
*
* public CordovaResourceApi.OpenForReadResult handleOpenForRead(Uri uri) throws IOException {
* Uri origUri = fromPluginUri(uri);
* ...
* }
* </pre>
*/
public Uri remapUri(Uri uri) {
return null;
}
/**
* Called to handle CordovaResourceApi.openForRead() calls for a cdvplugin://pluginId/ URL.
* Should never return null.
* Added in cordova-android@4.0.0
*/
public CordovaResourceApi.OpenForReadResult handleOpenForRead(Uri uri) throws IOException {
throw new FileNotFoundException("Plugin can't handle uri: " + uri);
}
/**
* Refer to remapUri()
* Added in cordova-android@4.0.0
*/
protected Uri toPluginUri(Uri origUri) {
return new Uri.Builder()
.scheme(CordovaResourceApi.PLUGIN_URI_SCHEME)
.authority(serviceName)
.appendQueryParameter("origUri", origUri.toString())
.build();
}
/**
* Refer to remapUri()
* Added in cordova-android@4.0.0
*/View on GitHub (pinned to 7c1e190064)
Solutions
- If your plugin means to serve plugin:// URLs, override handleOpenForRead in your CordovaPlugin subclass and return a non-null OpenForReadResult (e.g. open the origUri query parameter)
Example fix
// before: plugin uses toPluginUri() but never overrides handleOpenForRead
public class MyPlugin extends CordovaPlugin { }
// after
@Override
public CordovaResourceApi.OpenForReadResult handleOpenForRead(Uri uri) throws IOException {
String orig = uri.getQueryParameter("origUri");
return webView.getResourceApi().openForRead(Uri.parse(orig));
} Defensive patterns
Strategy: type-guard
Type guard
// a plugin can serve plugin:// URIs only if it overrides handleOpenForRead
public boolean canServePluginUris() {
try {
return !getClass().getMethod("handleOpenForRead", Uri.class).getDeclaringClass()
.equals(CordovaPlugin.class);
} catch (NoSuchMethodException e) { return false; }
} Try / catch
try {
result = plugin.handleOpenForRead(uri);
} catch (FileNotFoundException e) {
// plugin did not implement handleOpenForRead — fall back to the origUri or fail soft
} Prevention
- Always implement handleOpenForRead when using toPluginUri/remapUri
- Return the origUri passthrough as a minimal safe implementation
- Test plugin:// URL loads on a real device, not just the emulator
When it happens
Trigger: A plugin calls toPluginUri(someUri) and the resulting cdvplugin://<serviceName>/...?origUri=... URL is later opened (e.g. fed to the WebView, an <img>, or CordovaResourceApi.openForRead) while the plugin never overrode handleOpenForRead(Uri) to return an OpenForReadResult.
Common situations: Plugin author copied remapUri()/toPluginUri() from another plugin but not the matching handleOpenForRead override; URL interception pattern (bridge via plugin URIs) half-implemented after a Cordova upgrade; third-party plugin incompatible with the resource API.
Related errors
- Invalid plugin ID in URI: ${uri}
- URI not supported by CordovaResourceApi: ${uri}
- Relative URIs are not supported.
- Do not perform IO operations on the UI thread. Use CordovaIn
- Tried to perform an IO operation on the WebCore thread. Use
AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22).
Data as JSON: /api/errors/0c53328304e6ceff.
Report an issue: GitHub.