jackwener/OpenCLI · error

Directory "${targetDir}" already exists and is not empty.

Error message

Directory "${targetDir}" already exists and is not empty.

What it means

createPluginScaffold resolves the target directory (opts.dir or the plugin name) and refuses to proceed if it already exists and is non-empty, to avoid clobbering existing files. An empty existing directory is allowed and simply reused; a non-empty one throws this Error before any files are written.

Source

Thrown at src/plugin-scaffold.ts:49

/**
 * Create a new plugin scaffold directory.
 */
export function createPluginScaffold(name: string, opts: ScaffoldOptions = {}): ScaffoldResult {
  // Validate name
  if (!/^[a-z][a-z0-9-]*$/.test(name)) {
    throw new Error(
      `Invalid plugin name "${name}". ` +
      `Plugin names must start with a lowercase letter and contain only lowercase letters, digits, and hyphens.`
    );
  }

  const targetDir = opts.dir
    ? path.resolve(opts.dir)
    : path.resolve(name);

  if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
    throw new Error(`Directory "${targetDir}" already exists and is not empty.`);
  }

  fs.mkdirSync(targetDir, { recursive: true });

  const files: string[] = [];

  // opencli-plugin.json
  const manifest = {
    name,
    version: '0.1.0',
    description: opts.description ?? `An opencli plugin: ${name}`,
    opencli: `>=${PKG_VERSION}`,
  };
  writeFile(targetDir, 'opencli-plugin.json', JSON.stringify(manifest, null, 2) + '\n');
  files.push('opencli-plugin.json');

  // package.json
  const pkg = {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Delete or move the existing directory, then rerun the scaffold
  2. Pass a different opts.dir (or plugin name) pointing at a fresh path
  3. If reuse is intended, empty the directory first — an existing but empty directory is accepted

Example fix

// before
createPluginScaffold('my-plugin'); // second run, dir already populated
// after
rm -rf ./my-plugin && createPluginScaffold('my-plugin');
// or target a fresh location
createPluginScaffold('my-plugin', { dir: './plugins/my-plugin-v2' });
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'fs'; import path from 'path';
const targetDir = opts.dir ? path.resolve(opts.dir) : path.resolve(name);
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
  throw new Error(`target not empty: ${targetDir}`);
}

Type guard

function isWritableFreshDir(dir: string): boolean {
  return !fs.existsSync(dir) || fs.readdirSync(dir).length === 0;
}

Try / catch

try {
  createPluginScaffold(name, opts);
} catch (e) {
  if (e instanceof Error && e.message.includes('already exists and is not empty')) {
    console.error('Pick a new --dir or remove/relocate the existing directory.');
  } else throw e;
}

Prevention

When it happens

Trigger: Running the scaffold twice for the same plugin name without deleting the first output, or pointing opts.dir at an existing populated directory such as the current repo folder.

Common situations: Re-running a failed/partial scaffold after a first attempt partially populated the directory, choosing the project root as opts.dir, leftover files from a previous experiment with the same plugin name.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/83af4e100d6667b3. Report an issue: GitHub.