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
- Fix the outputName function so each source maps to a unique relative path (e.g. include the source's directory in the name)
- Check whether outputRoot or intermediate directories are symlinks collapsing distinct paths, and use a non-aliased outputRoot
- 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
- Derive output names from the full source-relative path, not just the basename
- Keep outputRoot free of symlinks that could canonicalize two names to one path
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
- Duplicate output path: ${outputPath}
- Output path must identify a file below outputRoot: ${outputP
- Refusing to overwrite a symbolic link: ${outputPath}
- Output path is not a regular file: ${outputPath}
- Output path already exists; enable overwrite explicitly: ${o
AI-assisted analysis of ReactiveX/rxjs@54796b38a5 (2026-08-28).
Data as JSON: /api/errors/9e524273c7ea7f27.
Report an issue: GitHub.