{"record":{"id":"7830051efa101c48","repo":"apache/cordova-android","slug":"invalid-plugin-id-in-uri-uri","errorCode":null,"errorMessage":"Invalid plugin ID in URI: ${uri}","messagePattern":"Invalid plugin ID in URI: (.+?)","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"error","filePath":"framework/src/org/apache/cordova/CordovaResourceApi.java","lineNumber":325,"sourceCode":"                conn.setDoInput(true);\n                String mimeType = conn.getHeaderField(\"Content-Type\");\n                if (mimeType != null) {\n                    mimeType = mimeType.split(\";\")[0];\n                }\n                int length = conn.getContentLength();\n                InputStream inputStream;\n                if (\"gzip\".equals(conn.getContentEncoding())) {\n                    inputStream = new GZIPInputStream(conn.getInputStream());\n                } else {\n                    inputStream = conn.getInputStream();\n                }\n                return new OpenForReadResult(uri, inputStream, mimeType, length, null);\n            }\n            case URI_TYPE_PLUGIN: {\n                String pluginId = uri.getHost();\n                CordovaPlugin plugin = pluginManager.getPlugin(pluginId);\n                if (plugin == null) {\n                    throw new FileNotFoundException(\"Invalid plugin ID in URI: \" + uri);\n                }\n                return plugin.handleOpenForRead(uri);\n            }\n        }\n        throw new FileNotFoundException(\"URI not supported by CordovaResourceApi: \" + uri);\n    }\n\n    public OutputStream openOutputStream(Uri uri) throws IOException {\n        return openOutputStream(uri, false);\n    }\n\n    /**\n     * Opens a stream to the given URI.\n     *\n     * @return Never returns null.\n     * @throws IllegalArgumentException For relative URIs. Relative URIs should be resolved before\n     *                                  being passed into this function.\n     * @throws IOException              If the URI cannot be opened.","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/apache/cordova-android/blob/7c1e190064e349ffa4bbc6ac37b77cd773e4dbd3/framework/src/org/apache/cordova/CordovaResourceApi.java#L307-L343","documentation":"When openForRead receives a URI of type PLUGIN (cdvplugin://...), CordovaResourceApi takes the URI host as the plugin id, looks it up via pluginManager.getPlugin(id), and throws FileNotFoundException if no plugin with that service name is registered. The host segment must equal the plugin's <name> (its serviceName), and the plugin must actually be initialized.","triggerScenarios":"Calling openForRead on a cdvplugin:// URI whose authority is not a loaded plugin: wrong/renamed <name> in plugin.xml (serviceName is derived from it), plugin removed/failed to initialize, URI hand-built with a mistyped authority, or plugin URIs constructed while the plugin has not finished initializing.","commonSituations":"Plugin renamed between versions so persisted cdvplugin:// URLs (cached in the WebView, localStorage, database blobs) no longer resolve; a plugin disabled at runtime; inter-plugin code guessing another plugin's service name.","solutions":["Make the URI authority exactly match the target plugin's <name> from plugin.xml — preferably build URIs with that plugin's toPluginUri()/PluginEntry rather than string concatenation","Verify the plugin is actually loaded (check pluginInitialize ran / it appears in the plugin registry) before issuing reads of its URIs"],"exampleFix":"// before: hand-built URI with wrong plugin id\nUri u = Uri.parse(\"cdvplugin://my-plugin/foo\");\nresourceApi.openForRead(u);\n\n// after: derive the URI from the plugin instance\nUri u = myPluginInstance.toPluginUri(origUri);\nresourceApi.openForRead(u);","handlingStrategy":"type-guard","validationCode":"// before openForRead on a plugin URI, verify the plugin is live\nString pluginId = uri.getHost();\nif (webView.getPluginManager().getPlugin(pluginId) == null) {\n    throw new IllegalStateException(\"plugin not loaded: \" + pluginId);\n}","typeGuard":"static boolean isResolvablePluginUri(CordovaWebView webView, Uri uri) {\n    return \"cdvplugin\".equals(uri.getScheme())\n        && webView.getPluginManager().getPlugin(uri.getHost()) != null;\n}","tryCatchPattern":"try {\n    OpenForReadResult r = resourceApi.openForRead(uri);\n} catch (FileNotFoundException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Invalid plugin ID\")) {\n        // plugin missing/renamed: re-derive URI via toPluginUri() from the live plugin\n    }\n}","preventionTips":["Build plugin URIs with toPluginUri() instead of string concatenation","Keep <name> in plugin.xml stable across releases","Don't persist cdvplugin:// URLs — they break when plugins are renamed/removed"],"tags":["cordova","cordova-android","java","plugin","uri","resource-api"],"backgroundTag":"unknown-plugin-id","analyzedSha":"7c1e190064e349ffa4bbc6ac37b77cd773e4dbd3","analyzedAt":"2026-08-22T04:57:58.868Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}