{"record":{"id":"957ec34f0170aff4","repo":"apple/pkl","slug":"output-file-conflict-output-files-entries-p-957ec3","errorCode":null,"errorMessage":"Output file conflict: `output.files` entries `\"${previousOutput.pathSpec}\"` in module `${previousOutput.moduleUri}` and `\"$pathSpec\"` in module `$moduleUri` resolve to the same file path `$realPath`.","messagePattern":"Output file conflict: `output\\.files` entries `\"(.+?)\"` in module `(.+?)` and `\"\\$pathSpec\"` in module `\\$moduleUri` resolve to the same file path `\\$realPath`\\.","errorType":"exception","errorClass":"CliException","httpStatus":null,"severity":"error","filePath":"pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt","lineNumber":251,"sourceCode":"      for ((moduleUri, outputDir) in outputDirs) {\n        if (outputDir.exists() && !outputDir.isDirectory()) {\n          throw CliException(\"Output path `$outputDir` exists and is not a directory.\")\n        }\n        val moduleSource = toModuleSource(moduleUri, inputStream)\n        val output = evaluator.evaluateOutputFiles(moduleSource)\n        val realOutputDir = if (outputDir.exists()) outputDir.toRealPath() else outputDir\n\n        for ((pathSpec, fileOutput) in output) {\n          checkPathSpec(pathSpec)\n          val (realPath, resolvedPath) = realOutputDir.resolveRealPath(Path.of(pathSpec))\n          if (!realPath.startsWith(realOutputDir)) {\n            throw CliException(\n              \"Output file conflict: `output.files` entry `\\\"$pathSpec\\\"` in module `$moduleUri` resolves to file path `$realPath`, which is outside output directory `$realOutputDir`.\"\n            )\n          }\n          val previousOutput = writtenFiles[realPath]\n          if (previousOutput != null) {\n            throw CliException(\n              \"Output file conflict: `output.files` entries `\\\"${previousOutput.pathSpec}\\\"` in module `${previousOutput.moduleUri}` and `\\\"$pathSpec\\\"` in module `$moduleUri` resolve to the same file path `$realPath`.\"\n            )\n          }\n          if (realPath.isDirectory()) {\n            throw CliException(\n              \"Output file conflict: `output.files` entry `\\\"$pathSpec\\\"` in module `$moduleUri` resolves to file path `$realPath`, which is a directory.\"\n            )\n          }\n          writtenFiles[realPath] = OutputFile(pathSpec, moduleUri)\n          realPath.createParentDirectories()\n          realPath.writeBytes(fileOutput.bytes)\n          outputStream.writeText(\n            IoUtils.relativize(resolvedPath, currentWorkingDir).toString() +\n              IoUtils.getLineSeparator()\n          )\n          outputStream.flush()\n        }\n      }","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt#L233-L269","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// module A before\noutput.files { [\"out.json\"] }\n// module B before\noutput.files { [\"./out.json\"] }\n// after: module B\noutput.files { [\"out-b.json\"] }","handlingStrategy":"validation","validationCode":"val seen = mutableSetOf<Path>()\nfor (spec in allOutputPathSpecs()) {\n  val real = outDir.resolveRealPath(spec).realPath\n  check(seen.add(real)) { \"duplicate output path: $spec\" }\n}","typeGuard":null,"tryCatchPattern":"try {\n  pklCli.runEval(args)\n} catch (e: CliException) {\n  if (\"resolve to the same file path\" in (e.message ?: \"\")) dedupeOutputsAndRetry()\n  else throw e\n}","preventionTips":["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."],"tags":["cli","file-conflict","duplicate-output"],"backgroundTag":"file-already-exists","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}