apple/pkl · error · CliException
Output file conflict: `output.files` entry `"$pathSpec"` in…
Error message
Output file conflict: `output.files` entry `"$pathSpec"` in module `$moduleUri` resolves to file path `$realPath`, which is a directory.
What it means
An `output.files` entry resolves to a path that currently is a directory on disk, so the CLI cannot write bytes to it and throws from writeMultipleFileOutput. This happens after the outside-directory and duplicate-path checks pass.
Solutions
- Change the path spec to include a filename inside the directory, e.g. `build/output.json`.
- Remove or rename the existing directory at the resolved path if it is stale.
- Check the resolved path printed in the message to confirm what the CLI attempted to write.
Example fix
// before
output.files { ["build"] }
// after
output.files { ["build/output.json"] } Defensive patterns
Strategy: validation
Validate before calling
val target = outDir.resolveRealPath(pathSpec).realPath
check(!java.nio.file.Files.isDirectory(target)) { "$pathSpec resolves to a directory" } Try / catch
try {
pklCli.runEval(args)
} catch (e: CliException) {
if ("which is a directory" in (e.message ?: "")) fixPathToFileName(e)
else throw e
} Prevention
- Always include a filename in output.files path specs.
- Clean stale directories from the output dir before evaluating.
- After renames, check the output dir for paths that became directories.
When it happens
Trigger: Path spec `foo` when `foo/` exists as a directory in the output directory, or a path spec that omits the filename while a directory of that name exists (e.g. `output.files { ["build"] }`).
Common situations: After refactoring, an output previously written to `build/out.json` becomes `build`, which is now a directory; or users mistake the files map value for a directory target instead of a file path.
Related errors
- Output file conflict: `output.files` entries
- 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/39998499dfb7e743.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt:256
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()
}
}
}
}
/**
* Resolves [rel] against this Path name-by-name. At each step, the real path is resolved if theView on GitHub (pinned to f3efcbfc9b)