apache/cordova-android · error · CordovaError

Required attribute "target-dir" not specified in <source-fil

Error message

Required attribute "target-dir" not specified in <source-file> element from plugin: ${plugin.id}

What it means

The second validation in the 'source-file' installer: after src is present, the element must also declare target-dir, which decides the destination path inside the Android project (getInstallDestination builds it from target-dir). Thrown during plugin install when target-dir is missing or empty.

Source

Thrown at lib/pluginHandlers.js:30

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

const fs = require('node:fs');
const path = require('node:path');
const isPathInside = require('is-path-inside');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;

const handlers = {
    'source-file': {
        install: function (obj, plugin, project, options) {
            if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
            if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));

            const dest = getInstallDestination(obj);

            if (options && options.force) {
                copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
            } else {
                copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
            }
        },
        uninstall: function (obj, plugin, project, options) {
            const dest = getInstallDestination(obj);

            // TODO: Add Koltin extension to uninstall, since they are handled like Java files
            if (obj.src.endsWith('java')) {
                deleteJava(project.projectDir, dest);
            } else {
                // Just remove the file, not the whole parent directory
                removeFileF(path.resolve(project.projectDir, dest));

View on GitHub (pinned to 7c1e190064)

Solutions

  1. Add target-dir naming the destination under the Android project: <source-file src="src/android/Foo.java" target-dir="src/com/example/foo" />.
  2. Use the conventional location matching the Java package (src/<package path>) so the sources compile.
  3. If the file is a resource rather than compiled source, move it to a <resource-file> element targeting a res/ or assets/ directory instead.
  4. Patch or pin the offending third-party plugin.

Example fix

<!-- before -->
<source-file src="src/android/Foo.java" />

<!-- after -->
<source-file src="src/android/Foo.java" target-dir="src/com/example/foo" />
Defensive patterns

Strategy: validation

Validate before calling

const et = require('elementtree');
const pluginXml = et.parse(fs.readFileSync('plugin.xml', 'utf8').toString());
for (const el of pluginXml.getroot().iter('source-file')) {
  if (!el.attrib['target-dir']) {
    throw new Error(`<source-file src="${el.attrib.src}"> is missing target-dir="..."`);
  }
}

Type guard

const isValidSourceFileElement = (el) =>
  Boolean(el && el.tag === 'source-file' && el.attrib && el.attrib.src && el.attrib['target-dir']);

Try / catch

try {
  await api.addPlugin(pluginInfo);
} catch (e) {
  if (/Required attribute "target-dir" not specified in <source-file>/.test(e.message)) {
    // add target-dir matching the Java package path, e.g. src/com/example, then retry
  }
  throw e;
}

Prevention

When it happens

Trigger: `cordova plugin add <plugin>` where plugin.xml has <source-file src="..."/> with no target-dir attribute - the src check passes, then this one fires.

Common situations: Plugin authors assuming a default destination exists (there is none - the copy needs an explicit target); partial migration of an old plugin.xml; elements copied from <resource-file> which does not require target-dir.

Related errors


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