ReactiveX/rxjs · error · Error
${label} is not a directory: ${path}
Error message
${label} is not a directory: ${path} What it means
Thrown by canonicalDirectory when the given source root exists (realpath resolved) but is not a directory — typically a regular file. The migrate CLI requires real directories for source/output roots before planning a migration.
Source
Thrown at packages/migrate/src/node.ts:193
if (!outputName || outputName === '.' || isAbsolute(outputName)) {
throw new Error(`Output name must be a non-empty relative path: ${outputName || '<empty>'}`);
}
const outputPath = resolve(outputRoot, outputName);
if (outputPath === outputRoot) throw new Error(`Output name must identify a file below outputRoot: ${outputName}`);
assertContained(outputRoot, outputPath, `Output path is outside outputRoot: ${outputName}`);
return outputPath;
}
function assertContained(root: string, candidate: string, message: string): void {
const localPath = relative(root, candidate);
if (localPath === '' || (localPath !== '..' && !localPath.startsWith(`..${sep}`) && !isAbsolute(localPath))) return;
throw new Error(message);
}
async function canonicalDirectory(path: string, label: string): Promise<string> {
const canonicalPath = await realpath(path);
const pathStats = await stat(canonicalPath);
if (!pathStats.isDirectory()) throw new Error(`${label} is not a directory: ${path}`);
return canonicalPath;
}
async function canonicalFutureDirectory(path: string, label: string): Promise<string> {
const resolved = await canonicalFuturePath(path);
try {
const pathStats = await stat(path);
if (!pathStats.isDirectory()) throw new Error(`${label} is not a directory: ${path}`);
} catch (error: unknown) {
if (!isMissingPathError(error)) throw error;
}
return resolved;
}
/**
* Resolves every existing prefix through realpath and appends only missing
* path segments. This detects an existing symlink that redirects a future
* output outside its declared root without creating any directory first.View on GitHub (pinned to 54796b38a5)
Solutions
- Pass a directory path (e.g. 'src' rather than 'src/index.ts') to the source-root option
- Verify with ls/stat that the path is a directory
- Fix typos in the configured path
Example fix
// before
await canonicalSourceRoot('src/index.ts');
// after
await canonicalSourceRoot('src'); Defensive patterns
Strategy: validation
Validate before calling
import { stat } from 'node:fs/promises';
if (!(await stat(srcRoot)).isDirectory()) throw new TypeError(`srcRoot must be a directory: ${srcRoot}`); Type guard
const isDirectory = async (p: string) => (await stat(p)).isDirectory();
Prevention
- Validate directories before passing them to the migrate CLI
- Prefer path completion from config rather than hand-typed paths
When it happens
Trigger: Calling canonicalSourceRoot with a path that exists but is a file, e.g. --src src/index.ts instead of a directory.
Common situations: Pointing --src or an input option at a single file, a typo'd path that matches a file, or a broken symlink resolution landing on a file.
Related errors
- Output name must be a non-empty relative path: ${outputName
- Output name must identify a file below outputRoot: ${outputN
- ${message}
- An output path ancestor is not a directory: ${existing}
- Project root is not a directory: ${projectRoot}
AI-assisted analysis of ReactiveX/rxjs@54796b38a5 (2026-08-28).
Data as JSON: /api/errors/e1a47cfa284062a0.
Report an issue: GitHub.