{"record":{"id":"4a8871a0bfe361e7","repo":"ReactiveX/rxjs","slug":"output-path-is-not-a-regular-file-outputpath","errorCode":null,"errorMessage":"Output path is not a regular file: ${outputPath}","messagePattern":"Output path is not a regular file: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/migrate/src/node.ts","lineNumber":159,"sourceCode":"  const canonicalOutputs = new Set<string>();\n  for (const { sourcePath, outputPath, result } of plan.files) {\n    if (result.status === 'refused') throw new Error(`Migration result was refused for source: ${sourcePath}`);\n    const resolvedOutputPath = resolve(outputPath);\n    if (resolvedOutputPath === outputRoot) throw new Error(`Output path must identify a file below outputRoot: ${outputPath}`);\n    assertContained(outputRoot, resolvedOutputPath, `Output path is outside outputRoot: ${outputPath}`);\n    const canonicalOutputPath = await canonicalFuturePath(resolvedOutputPath);\n    assertContained(canonicalOutputRoot, canonicalOutputPath, `Output path resolves outside outputRoot: ${outputPath}`);\n    if (canonicalOutputs.has(canonicalOutputPath)) throw new Error(`Duplicate output path: ${outputPath}`);\n    canonicalOutputs.add(canonicalOutputPath);\n    await assertWritableOutput(resolvedOutputPath, options.overwrite ?? false);\n  }\n}\n\nasync function assertWritableOutput(outputPath: string, overwrite: boolean): Promise<void> {\n  try {\n    const outputStats = await lstat(outputPath);\n    if (outputStats.isSymbolicLink()) throw new Error(`Refusing to overwrite a symbolic link: ${outputPath}`);\n    if (!outputStats.isFile()) throw new Error(`Output path is not a regular file: ${outputPath}`);\n    if (!overwrite) throw new Error(`Output path already exists; enable overwrite explicitly: ${outputPath}`);\n  } catch (error: unknown) {\n    if (!isMissingPathError(error)) throw error;\n  }\n}\n\nexport async function migrateTestFiles(options: MigrateFilesOptions): Promise<readonly MigratedFile[]> {\n  if (options.write && !options.outputRoot) {\n    throw new Error('outputRoot is required when write is enabled.');\n  }\n  const plan = await planMigrationFiles(options);\n  return options.write ? applyMigrationPlan(plan, { overwrite: options.overwrite }) : plan.files;\n}\n\nfunction safeOutputPath(outputRoot: string, outputName: string): string {\n  if (!outputName || outputName === '.' || isAbsolute(outputName)) {\n    throw new Error(`Output name must be a non-empty relative path: ${outputName || '<empty>'}`);\n  }","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/migrate/src/node.ts#L141-L177","documentation":"When a planned output path already exists and is neither a symlink nor a regular file (i.e. it is a directory, FIFO, socket, or device), assertWritableOutput refuses to proceed. Writing file bytes over such an entry is never safe — especially a directory — so the migrator aborts before touching anything.","triggerScenarios":"Calling applyMigrationPlan when a planned files[].outputPath exists and lstat reports a non-file, non-symlink type; most commonly outputPath names an existing directory.","commonSituations":"An outputName function that drops the file extension, making outputPath collide with an existing directory of the same name; leftover directories from a previous tool run.","solutions":["Remove or rename the directory currently occupying the output path","Fix outputName so it always produces a file-like name with an extension","Choose a different outputRoot that does not contain conflicting directory names"],"exampleFix":"// before\noutputName: ({ sourcePath }) => basename(sourcePath, '.spec.ts'), // collides with dir\n\n// after\noutputName: ({ sourcePath }) => basename(sourcePath, '.spec.ts') + '.spec.ts',","handlingStrategy":"validation","validationCode":"import { lstat } from 'node:fs/promises';\nasync function allOutputsAreRegularFilesOrMissing(plan: { files: { outputPath: string }[] }): Promise<boolean> {\n  for (const f of plan.files) {\n    try { if (!(await lstat(f.outputPath)).isFile()) return false; }\n    catch (e) { if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e; }\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure outputName always yields a filename with an extension","Check the output tree for pre-existing directories with colliding names before running"],"tags":["migrate","filesystem","write-safety","path-validation"],"backgroundTag":"output-path-not-regular-file","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}