apple/pkl · error · CliException

Output path `$outputDir` exists and is not a directory.

Error message

Output path `$outputDir` exists and is not a directory.

What it means

In multiple-file output mode, CliEvaluator.writeMultipleFileOutput validates each per-module output directory: if the target directory path exists but is not a directory (e.g. a regular file), it throws CliException because file outputs cannot be placed there.

Source

Thrown at pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt:235

  private fun toModuleSource(uri: URI, reader: InputStream) =
    if (uri == VmUtils.REPL_TEXT_URI) {
      ModuleSource.create(uri, reader.readAllBytes().toString(StandardCharsets.UTF_8))
    } else {
      ModuleSource.uri(uri)
    }

  /**
   * Renders each module's `output.files`, writing each entry as a file into the specified output
   * directory.
   */
  private fun writeMultipleFileOutput(builder: EvaluatorBuilder) {
    val outputDirs = directoryOutputPaths!!
    val writtenFiles = mutableMapOf<Path, OutputFile>()
    builder.setOutputFormat(options.outputFormat).build().use { evaluator ->
      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`."
            )

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Delete or move the existing file at the reported outputDir path so a directory can be created.
  2. Verify the --multiple-file-output-path argument points to a directory location.
  3. Add cleanup (`rm -f <path>` or equivalent) to scripts before multi-file output runs.
  4. Separate single-file and multi-file output locations to avoid collisions.

Example fix

# before
$ pkl eval -m build/out ...   # build/out is a stale file
# after
$ rm build/out && mkdir -p build/out && pkl eval -m build/out ...
Defensive patterns

Strategy: validation

Validate before calling

import java.nio.file.Files
import java.nio.file.Path
for ((_, dir) in outputDirs) {
  require(!Files.exists(dir) || Files.isDirectory(dir)) { "Output path $dir exists and is not a directory" }
}

Try / catch

try {
  ev.writeMultipleFileOutput(builder)
} catch (e: CliException) {
  System.err.println(e.message)
}

Prevention

When it happens

Trigger: Running pkl with --multiple-file-output-path(s) where one of the resolved directory output paths exists as a regular file — commonly a leftover single-file output or a wrong flag value.

Common situations: Previous runs wrote a file at the path now used as the multi-file output dir; CI caches a stale file; a typo merges the output dir path with a filename.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/4f7d7ffe4ba161eb. Report an issue: GitHub.