apache/cordova-android · error · CordovaError
File "${src}" is located outside the plugin directory "${plu
Error message
File "${src}" is located outside the plugin directory "${plugin_dir}" What it means
A security check in copyFile: it resolves both the source file and the plugin directory through fs.realpathSync (following symlinks) and requires the file to be inside the plugin directory via isPathInside. It throws when a plugin tries to copy a file that really lives outside its own tree, blocking path-traversal-style plugin content.
Source
Thrown at lib/pluginHandlers.js:215
events.emit('verbose', '<' + type + '> is not supported for android plugins');
};
module.exports.getUninstaller = function (type) {
if (handlers[type] && handlers[type].uninstall) {
return handlers[type].uninstall;
}
events.emit('verbose', '<' + type + '> is not supported for android plugins');
};
function copyFile (plugin_dir, src, project_dir, dest, link) {
src = path.resolve(plugin_dir, src);
if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!');
// check that src path is inside plugin directory
const real_path = fs.realpathSync(src);
const real_plugin_path = fs.realpathSync(plugin_dir);
if (!isPathInside(real_path, real_plugin_path)) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory
if (!isPathInside(dest, project_dir)) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
fs.mkdirSync(path.dirname(dest), { recursive: true });
if (link) {
symlinkFileOrDirTree(src, dest);
} else {
fs.cpSync(src, dest, { recursive: true });
}
}
// Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
const target_path = path.resolve(project_dir, dest);
if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }View on GitHub (pinned to 7c1e190064)
Solutions
- Move or copy the referenced file physically inside the plugin directory and use a src relative to the plugin root without ../ segments
- For local development, remove symlinks: uninstall the plugin, then `cordova plugin add /abs/path/to/plugin` (real path) instead of npm link
- If shared code is needed across plugins, publish it as its own plugin and declare it as a <dependency> instead of reaching outside
Example fix
# before <resource-file src="../../shared/assets/logo.png" target="res/drawable/logo.png" /> # after (copy shared/assets/logo.png into the plugin first) <resource-file src="src/shared/logo.png" target="res/drawable/logo.png" />
Defensive patterns
Strategy: validation
Validate before calling
// before install: flag src paths that escape the plugin root
const path = require('path');
const escapes = srcAttr.split('/').filter(p => p === '..').length > 0;
if (escapes) throw new Error('plugin.xml src escapes the plugin dir and will be rejected'); Try / catch
try { await cordova.plugin('add', p); } catch (e) {
if (/located outside the plugin directory/.test(e.message)) { /* inline the file into the plugin or use a <dependency> plugin */ }
} Prevention
- Never reference files outside the plugin directory from plugin.xml
- Avoid npm link for local plugin testing; add plugins by real path
- Audit third-party plugin.xml for ../ paths before installing
When it happens
Trigger: A plugin.xml src containing ../ escapes (e.g. <resource-file src="../../shared/logo.png"/>), or a file inside the plugin that is a symlink pointing outside (e.g. npm link-ed local plugins, or a symlinked node_modules dependency file). The realpath check sees through the symlink and rejects it.
Common situations: Developing plugins with `npm link` or a symlinked plugins/<id> directory during local testing; monorepo plugins referencing shared assets via ../; a repo checked out with symlinked submodules.
Related errors
- Destination "${dest}" for source file "${src}" is located ou
- Required attribute "src" not specified in <framework> elemen
- Required attribute "src" not specified in <asset> element fr
- Required attribute "target" not specified in <asset> element
- "${src}" not found!
AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22).
Data as JSON: /api/errors/bfda9d0fbeadc4b4.
Report an issue: GitHub.