apple/pkl · error · CliException
Output file conflict: `output.files` entries
Error message
Output file conflict: `output.files` entries `"${previousOutput.pathSpec}"` in module `${previousOutput.moduleUri}` and `"$pathSpec"` in module `$moduleUri` resolve to the same file path `$realPath`. What it means
Two `output.files` entries (possibly from different modules) resolve to the same real file path, so the CLI aborts instead of letting one silently overwrite the other. writeMultipleFileOutput tracks every written path in `writtenFiles` keyed by the normalized real path and throws when a collision is found.
Solutions
- Rename one of the conflicting `output.files` entries so each resolves to a unique path.
- If overwrite is intended, change the setup so only one entry targets the file, or move to a single-module output.
- Normalize path specs (remove `./`, `..`) to spot accidental duplicates across modules.
Example fix
// module A before
output.files { ["out.json"] }
// module B before
output.files { ["./out.json"] }
// after: module B
output.files { ["out-b.json"] } Defensive patterns
Strategy: validation
Validate before calling
val seen = mutableSetOf<Path>()
for (spec in allOutputPathSpecs()) {
val real = outDir.resolveRealPath(spec).realPath
check(seen.add(real)) { "duplicate output path: $spec" }
} Try / catch
try {
pklCli.runEval(args)
} catch (e: CliException) {
if ("resolve to the same file path" in (e.message ?: "")) dedupeOutputsAndRetry()
else throw e
} Prevention
- Keep a single registry of output files across modules.
- Normalize path specs (no `./`, `..`) when authoring modules.
- Review output.files entries when adding new modules to a multi-module repo.
When it happens
Trigger: Two modules declare output paths that normalize identically, e.g. `./out.pkl` and `out.pkl` in different modules, or `a/../out.txt` and `out.txt`, while evaluating with multiple input modules or projects.
Common situations: Multi-module monorepos where several PklProjects emit the same output filename; refactors that add a second module outputting to a shared path; glob-like duplicate path specs after directory reorganization.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Output file conflict: `output.files` entry `"$pathSpec"` in…
- Cannot download packages because no cache directory is…
- Cannot generate documentation for just one module within a…
- Cannot generate JUnit report for $moduleUri. A report with…
- Cannot substitute output path placeholder
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/957ec34f0170aff4.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt:251
for ((moduleUri, outputDir) in outputDirs) {
if (outputDir.exists() && !outputDir.isDirectory()) {
throw CliException("Output path `$outputDir` exists and is not a directory.")
}
val moduleSource = toModuleSource(moduleUri, inputStream)
val output = evaluator.evaluateOutputFiles(moduleSource)
val realOutputDir = if (outputDir.exists()) outputDir.toRealPath() else outputDir
for ((pathSpec, fileOutput) in output) {
checkPathSpec(pathSpec)
val (realPath, resolvedPath) = realOutputDir.resolveRealPath(Path.of(pathSpec))
if (!realPath.startsWith(realOutputDir)) {
throw CliException(
"Output file conflict: `output.files` entry `\"$pathSpec\"` in module `$moduleUri` resolves to file path `$realPath`, which is outside output directory `$realOutputDir`."
)
}
val previousOutput = writtenFiles[realPath]
if (previousOutput != null) {
throw CliException(
"Output file conflict: `output.files` entries `\"${previousOutput.pathSpec}\"` in module `${previousOutput.moduleUri}` and `\"$pathSpec\"` in module `$moduleUri` resolve to the same file path `$realPath`."
)
}
if (realPath.isDirectory()) {
throw CliException(
"Output file conflict: `output.files` entry `\"$pathSpec\"` in module `$moduleUri` resolves to file path `$realPath`, which is a directory."
)
}
writtenFiles[realPath] = OutputFile(pathSpec, moduleUri)
realPath.createParentDirectories()
realPath.writeBytes(fileOutput.bytes)
outputStream.writeText(
IoUtils.relativize(resolvedPath, currentWorkingDir).toString() +
IoUtils.getLineSeparator()
)
outputStream.flush()
}
}View on GitHub (pinned to f3efcbfc9b)