apache/cordova-android · error · CordovaError

Destination "${dest}" for source file "${src}" is located ou

Error message

Destination "${dest}" for source file "${src}" is located outside the project

What it means

The destination-side guard in copyFile: the resolved destination must be inside the project directory. A target/target-dir attribute containing ../ or an absolute path resolves outside platforms/android and the copy is refused with this CordovaError, protecting the filesystem from plugins writing outside the Cordova project.

Source

Thrown at lib/pluginHandlers.js:220

        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!'); }

    copyFile(plugin_dir, src, project_dir, dest, !!link);
}

function symlinkFileOrDirTree (src, dest) {

View on GitHub (pinned to 7c1e190064)

Solutions

  1. Change target-dir/target to a path relative to the Android project root without leading slash or ../, e.g. target-dir="src/com/example/plugin"
  2. Verify what the project root is for the element type (source-file: app/src/main/java...; resource-file: app/src/main/res...)
  3. Treat this error in a third-party plugin as a red flag: inspect its plugin.xml before working around it

Example fix

<!-- before -->
<resource-file src="hosts" target="/etc/hosts" />

<!-- after -->
<resource-file src="hosts" target="app/src/main/res/raw/hosts" />
Defensive patterns

Strategy: validation

Validate before calling

// target-dir/target must be relative and must not escape the project
const safe = t => !path.isAbsolute(t) && !t.split('/').includes('..');
if (!safe(targetDirAttr)) throw new Error('target escapes the Cordova project and will be rejected');

Try / catch

try { await cordova.plugin('add', p); } catch (e) {
  if (/located outside the project/.test(e.message)) { /* fix target/target-dir to a project-relative path */ }
}

Prevention

When it happens

Trigger: A plugin.xml element like <source-file src="x.java" target-dir="../../somewhere"/> or a target attribute resolving outside projectDir; also resource-file target="res/../../etc/hosts"-style paths. Thrown during `cordova plugin add` / `cordova prepare`.

Common situations: Plugin author mistakenly writes an install-absolute path (target-dir="/src/com/x") which path.resolve treats as absolute and escapes the project; ported iOS-style target paths; malicious or buggy third-party plugin.

Related errors


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