{"record":{"id":"e1a47cfa284062a0","repo":"ReactiveX/rxjs","slug":"label-is-not-a-directory-path","errorCode":null,"errorMessage":"${label} is not a directory: ${path}","messagePattern":"(.+?) is not a directory: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/migrate/src/node.ts","lineNumber":193,"sourceCode":"  if (!outputName || outputName === '.' || isAbsolute(outputName)) {\n    throw new Error(`Output name must be a non-empty relative path: ${outputName || '<empty>'}`);\n  }\n  const outputPath = resolve(outputRoot, outputName);\n  if (outputPath === outputRoot) throw new Error(`Output name must identify a file below outputRoot: ${outputName}`);\n  assertContained(outputRoot, outputPath, `Output path is outside outputRoot: ${outputName}`);\n  return outputPath;\n}\n\nfunction assertContained(root: string, candidate: string, message: string): void {\n  const localPath = relative(root, candidate);\n  if (localPath === '' || (localPath !== '..' && !localPath.startsWith(`..${sep}`) && !isAbsolute(localPath))) return;\n  throw new Error(message);\n}\n\nasync function canonicalDirectory(path: string, label: string): Promise<string> {\n  const canonicalPath = await realpath(path);\n  const pathStats = await stat(canonicalPath);\n  if (!pathStats.isDirectory()) throw new Error(`${label} is not a directory: ${path}`);\n  return canonicalPath;\n}\n\nasync function canonicalFutureDirectory(path: string, label: string): Promise<string> {\n  const resolved = await canonicalFuturePath(path);\n  try {\n    const pathStats = await stat(path);\n    if (!pathStats.isDirectory()) throw new Error(`${label} is not a directory: ${path}`);\n  } catch (error: unknown) {\n    if (!isMissingPathError(error)) throw error;\n  }\n  return resolved;\n}\n\n/**\n * Resolves every existing prefix through realpath and appends only missing\n * path segments. This detects an existing symlink that redirects a future\n * output outside its declared root without creating any directory first.","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/migrate/src/node.ts#L175-L211","documentation":"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.","triggerScenarios":"Calling canonicalSourceRoot with a path that exists but is a file, e.g. --src src/index.ts instead of a directory.","commonSituations":"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.","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"],"exampleFix":"// before\nawait canonicalSourceRoot('src/index.ts');\n// after\nawait canonicalSourceRoot('src');","handlingStrategy":"validation","validationCode":"import { stat } from 'node:fs/promises';\nif (!(await stat(srcRoot)).isDirectory()) throw new TypeError(`srcRoot must be a directory: ${srcRoot}`);","typeGuard":"const isDirectory = async (p: string) => (await stat(p)).isDirectory();","tryCatchPattern":null,"preventionTips":["Validate directories before passing them to the migrate CLI","Prefer path completion from config rather than hand-typed paths"],"tags":["filesystem","configuration","validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}