ruvnet/ruflo · error

Path is not a directory: ${resolvedPath}

Error message

Path is not a directory: ${resolvedPath}

What it means

validatePath() in the @claude-flow/codex CLI resolves the project-path argument and stats it. If the path exists but is not a directory (a regular file, symlink to a file, etc.) it throws this error. ENOENT is special-cased: a missing directory is created with fs.ensureDir and returned, so this message specifically means 'something exists at that path but it is not a folder'. Other stat failures (e.g. EACCES) rethrow as the raw OS error.

Source

Thrown at v3/@claude-flow/codex/src/cli.ts:42

  const errorMessage = error instanceof Error ? error.message : String(error);
  console.error(chalk.red.bold('\nError:'), chalk.red(message ?? errorMessage));

  if (error instanceof Error && error.stack && process.env.DEBUG) {
    console.error(chalk.gray('\nStack trace:'));
    console.error(chalk.gray(error.stack));
  }

  process.exit(1);
}

// Validate project path exists and is accessible
async function validatePath(projectPath: string): Promise<string> {
  const resolvedPath = path.resolve(projectPath);

  try {
    const stats = await fs.stat(resolvedPath);
    if (!stats.isDirectory()) {
      throw new Error(`Path is not a directory: ${resolvedPath}`);
    }
    return resolvedPath;
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
      // Directory doesn't exist, try to create it
      console.log(chalk.yellow(`Creating directory: ${resolvedPath}`));
      await fs.ensureDir(resolvedPath);
      return resolvedPath;
    }
    throw error;
  }
}

// Validate skill name format
function validateSkillName(name: string): boolean {
  const validPattern = /^[a-z][a-z0-9-]*$/;
  return validPattern.test(name);
}

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Point --path at the project root directory, not any file inside it.
  2. If you expected the directory to be auto-created, first remove the file occupying that exact path — auto-creation only triggers when the path does not exist (ENOENT).
  3. Verify with `ls -ld <path>` (or `test -d <path>`) before running the CLI.
  4. If the path lives under a file (e.g. file.txt/subdir), fix the parent structure — that surfaces as a different raw error but stems from the same mistake.

Example fix

# before
$ npx @claude-flow/codex init --path ./package.json
Error: Path is not a directory: /repo/package.json

# after
$ npx @claude-flow/codex init --path .
# (or point at an absent path to have it created automatically)
Defensive patterns

Strategy: validation

Validate before calling

import { stat } from 'node:fs/promises';
async function assertProjectDir(p: string): Promise<string> {
  try {
    const s = await stat(p);
    if (!s.isDirectory()) throw new Error(`refusing to use ${p}: it is a file, not a directory`);
    return p;
  } catch (e) {
    if ((e as NodeJS.ErrnoException).code === 'ENOENT') return p; // CLI will create it
    throw e; // EACCES etc.
  }
}
// wrap the CLI call:
await execFile('npx', ['@claude-flow/codex', 'init', '--path', await assertProjectDir(dir)]);

Try / catch

try {
  await runCodexCli(['--path', target]);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Path is not a directory:')) {
    // message carries the resolved absolute path — inspect it
    throw new Error(`--path must be a directory; got a file: ${err.message}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: (1) Passing a file as --path, e.g. `--path ./package.json` or `--path ./config/default.toml`; (2) a shell variable that was expanded from a file glob (`--path src/*.ts` matching one file); (3) a symlink at the target path pointing to a file; (4) a socket/device node occupying the path.

Common situations: CLI flags documented as 'project directory' being given a config file path by wrapper scripts; copy-pasted commands where the path was a file in the original tutorial; misconfigured CI variables holding an artifact path.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/027108fcde87c4d6. Report an issue: GitHub.