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

  1. 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

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


AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22). Data as JSON: /api/errors/0c53328304e6ceff. Report an issue: GitHub.