{"record":{"id":"17d3ddfb9f56a3a1","repo":"apple/pkl","slug":"cannot-substitute-output-path-placeholder-modul","errorCode":null,"errorMessage":"Cannot substitute output path placeholder `%{moduleDir}` because module `$uri` does not have a file system path.","messagePattern":"Cannot substitute output path placeholder `%(.+?)` because module `\\$uri` does not have a file system path\\.","errorType":"exception","errorClass":"PklException","httpStatus":null,"severity":"error","filePath":"pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt","lineNumber":135,"sourceCode":"\n    return options.base.normalizedSourceModules.associateWith { uri ->\n      val moduleDir: String? =\n        IoUtils.toPath(uri)?.let {\n          IoUtils.relativize(it.parent, workingDir).toString().ifEmpty { \".\" }\n        }\n      val moduleKey =\n        try {\n          moduleResolver.resolve(uri)\n        } catch (e: VmException) {\n          throw e.toPklException(stackFrameTransformer, options.base.color?.hasColor() ?: false)\n        }\n      val substituted =\n        pathStr\n          .replace(\"%{moduleName}\", IoUtils.inferModuleName(moduleKey))\n          .replace(\"%{outputFormat}\", options.outputFormat ?: \"%{outputFormat}\")\n          .replace(\"%{moduleDir}\", moduleDir ?: \"%{moduleDir}\")\n      if (substituted.contains(\"%{moduleDir}\")) {\n        throw PklException(\n          \"Cannot substitute output path placeholder `%{moduleDir}` \" +\n            \"because module `$uri` does not have a file system path.\"\n        )\n      }\n      val absolutePath = workingDir.resolve(substituted).normalize()\n      absolutePath\n    }\n  }\n\n  private fun Evaluator.evalOutput(moduleSource: ModuleSource): ByteArray {\n    if (options.expression == null) {\n      return evaluateOutputBytes(moduleSource)\n    }\n    return evaluateExpressionString(moduleSource, options.expression)\n      .toByteArray(StandardCharsets.UTF_8)\n  }\n\n  /** Renders each module's `output.bytes`, writing it to the specified output file. */","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-cli/src/main/kotlin/org/pkl/cli/CliEvaluator.kt#L117-L153","documentation":"When resolving per-module output file paths, the `%{moduleDir}` placeholder can only be substituted for modules that have a filesystem path (e.g. file:// modules). If a module came from a non-filesystem source (HTTP, classpath, virtual/repl module) and its pathStr still contains `%{moduleDir}` after substitution, CliEvaluator throws this error.","triggerScenarios":"Using `%{moduleDir}` in a multiple-file-output path option while evaluating modules loaded from jar/classpath, HTTP URLs, or stdin (no moduleDir), e.g. `pkl eval --multiple-file-output-path ... https://.../mod.pkl` with pathSpec \"%{moduleDir}/out.json\".","commonSituations":"Evaluating remote modules or modules from stdin while an output.files mapping references %{moduleDir}; reusing a config designed for local files against remote dependencies.","solutions":["Remove `%{moduleDir}` from the output pathSpec or replace it with a fixed directory.","Evaluate only local file-based modules when output paths depend on %{moduleDir}.","Materialize remote modules to disk first, then evaluate them as file modules.","Use `%{moduleName}` instead, which works for modules without a filesystem path."],"exampleFix":"// before\n[\"%{moduleDir}/out.json\"] = output // module has no file path\n// after\n[\"out/%{moduleName}.json\"] = output","handlingStrategy":"validation","validationCode":"// Ensure %{moduleDir} is only used for file-based modules\nif (pathStr.contains(\"%{moduleDir}\") && !moduleUri.scheme.startsWith(\"file\")) {\n  throw CliException(\"%{moduleDir} unsupported for non-file module $moduleUri\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  ev.writeMultipleFileOutput(...)\n} catch (e: PklException) {\n  if (e.message?.contains(\"%{moduleDir}\") == true) {\n    // switch pathSpec to %{moduleName} and retry\n  }\n}","preventionTips":["Never use %{moduleDir} in output paths when evaluating HTTP/classpath/stdin modules.","Prefer %{moduleName} for portable output path patterns.","Materialize remote modules to disk before filesystem-dependent output paths."],"tags":["cli","output-path","placeholder-substitution"],"backgroundTag":"invalid-config-value","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}