apple/pkl · error · CliException

Output file conflict: `output.files` entry `"$pathSpec"` res

Error message

Output file conflict: `output.files` entry `"$pathSpec"` resolves to file path `$realPath`, which is a directory.

What it means

writeMultipleFileOutput checks each resolved output path before writing; this error means one of the `output.files` pathSpec entries resolves to an existing directory, so the CLI cannot write a file at that path and aborts with a conflict error.

Source

Thrown at pkl-cli/src/main/kotlin/org/pkl/cli/CliCommandRunner.kt:129

    if (outputFiles.isEmpty()) return

    val writtenFiles = mutableMapOf<Path, String>()
    val outputDir = options.normalizedWorkingDir
    if (outputDir.exists() && !outputDir.isDirectory()) {
      throw CliException("Output path `$outputDir` exists and is not a directory.")
    }
    for ((pathSpec, fileOutput) in outputFiles) {
      checkPathSpec(pathSpec)
      val resolvedPath = outputDir.resolve(pathSpec).normalize()
      val realPath = if (resolvedPath.exists()) resolvedPath.toRealPath() else resolvedPath
      val previousOutput = writtenFiles[realPath]
      if (previousOutput != null) {
        throw CliException(
          "Output file conflict: `output.files` entries `\"${previousOutput}\"` and `\"$pathSpec\"` resolve to the same file path `$realPath`."
        )
      }
      if (realPath.isDirectory()) {
        throw CliException(
          "Output file conflict: `output.files` entry `\"$pathSpec\"` resolves to file path `$realPath`, which is a directory."
        )
      }
      writtenFiles[realPath] = pathSpec
      realPath.createParentDirectories()
      realPath.writeBytes(fileOutput.bytes)
      val displayPath =
        if (Path.of(pathSpec).isAbsolute) pathSpec
        else IoUtils.relativize(resolvedPath, currentWorkingDir).toString()
      errStream.writeText(displayPath + IoUtils.getLineSeparator())
      errStream.flush()
    }
  }

  class SynthesizedRunCommand(
    private val spec: CommandSpec,
    private val runner: CliCommandRunner,
    name: String? = null,

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Remove or rename the directory at the reported path, then re-run.
  2. Adjust the `output.files` pathSpec to a distinct file path that does not collide with an existing directory.
  3. Clean stale output directories between runs in CI scripts.
  4. Check placeholder substitution results — an empty/odd value can make a spec resolve to a directory.

Example fix

// before
["%{moduleName}"] = output // resolves to existing dir 'myapp'
// after
["%{moduleName}.json"] = output
Defensive patterns

Strategy: validation

Validate before calling

import java.nio.file.*
val p = outputDir.resolve(pathSpec).normalize()
require(!Files.isDirectory(p)) { "pathSpec $pathSpec resolves to existing directory $p" }

Try / catch

try {
  runner.writeMultipleFileOutput(outputFiles)
} catch (e: CliException) {
  // clean conflicting paths reported in e.message, then retry
}

Prevention

When it happens

Trigger: An `output.files` pathSpec (after placeholder substitution and path resolution against the output dir) matches an existing directory — e.g. pattern `"%{moduleName}"` where a directory with that name exists, or a previously-created directory layout left behind by another run.

Common situations: Reusing an output directory where a prior run created subdirectories; pathSpec patterns ending up empty so the spec resolves to the output dir itself; collisions between file output names and multi-file-output subdirectories.

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


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