ReactiveX/rxjs · error · Error

Duplicate output path: ${outputLocalName}

Error message

Duplicate output path: ${outputLocalName}

What it means

During planning, the migration computes an output path for each input file and tracks the canonical (realpath-resolved) destination of every output. If two different source files resolve to the same canonical output path, the second one triggers this error, because applying the plan would have one file silently clobber another. This is a planning-time integrity check in planMigrationFiles, so nothing has been written when it throws.

Source

Thrown at packages/migrate/src/node.ts:104

      canonicalSourcePath,
      localSourcePath,
      source: await readFile(canonicalSourcePath, 'utf8'),
    });
  }

  const files: MigratedFile[] = [];
  const canonicalOutputs = new Set<string>();
  for (const plannedSource of sources) {
    const outputLocalName = outputName({ sourcePath: plannedSource.sourcePath, sourceRoot, mode: outputMode });
    const outputPath = safeOutputPath(outputRoot, outputLocalName);
    const canonicalOutputPath = await canonicalFuturePath(outputPath);
    assertContained(canonicalOutputRoot, canonicalOutputPath, `Output path resolves outside outputRoot: ${outputLocalName}`);

    if (canonicalSources.has(canonicalOutputPath)) {
      throw new Error(`Output path aliases a source file: ${outputLocalName}`);
    }
    if (canonicalOutputs.has(canonicalOutputPath)) {
      throw new Error(`Duplicate output path: ${outputLocalName}`);
    }
    canonicalOutputs.add(canonicalOutputPath);

    const provenance: SourceProvenance = {
      repository: options.sourceRepository,
      sha: options.sourceSha,
      path: plannedSource.localSourcePath,
    };
    const result = migrateTestSource(plannedSource.source, {
      ...options,
      mode: options.mode,
      provenance,
      fileName: plannedSource.localSourcePath,
    });
    files.push({ sourcePath: plannedSource.sourcePath, outputPath, result });
  }

  return { sourceRoot, outputRoot, files };

View on GitHub (pinned to 54796b38a5)

Solutions

  1. Fix the outputName function so each source maps to a unique relative path (e.g. include the source's directory in the name)
  2. Check whether outputRoot or intermediate directories are symlinks collapsing distinct paths, and use a non-aliased outputRoot
  3. If only one file is intended, pass a single file instead of several

Example fix

// before
outputName: () => path.basename(sourcePath).replace(/\.ts$/, '.next.ts'),

// after
outputName: ({ sourcePath, sourceRoot }) =>
  path.relative(sourceRoot, sourcePath).replace(/\.ts$/, '.next.ts'),
Defensive patterns

Strategy: validation

Validate before calling

const seen = new Set<string>();
for (const input of options.files) {
  const name = outputName({ sourcePath: resolve(sourceRoot, input), sourceRoot, mode: options.mode ?? 'unselected' });
  const key = resolve(outputRoot, name);
  if (seen.has(key)) throw new Error(`duplicate output for ${input}`);
  seen.add(key);
}

Type guard

function isUniqueOutputNames(files: string[], outputName: (ctx: { sourcePath: string }) => string, outputRoot: string): boolean {
  const seen = new Set<string>();
  return files.every(f => {
    const key = resolve(outputRoot, outputName({ sourcePath: f }));
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

Try / catch

try {
  await migrateTestFiles(options);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Duplicate output path')) {
    // fix outputName to be source-unique, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling planMigrationFiles (or migrateTestFiles / apiPlan) with multiple files whose outputName function returns the same relative path for different sources, or returns paths that differ only by symlinked directories that canonicalize identically.

Common situations: A custom outputName callback that derives names from a basename (two files named foo.spec.ts in different directories), or outputRoot containing symlinks that collapse distinct relative paths onto one real location.

Related errors


AI-assisted analysis of ReactiveX/rxjs@54796b38a5 (2026-08-28). Data as JSON: /api/errors/9e524273c7ea7f27. Report an issue: GitHub.