OpenAPITools/openapi-generator · critical · RuntimeException
Target files must be generated within the output directory;
Error message
Target files must be generated within the output directory; absoluteTarget=%s outDir=%s
What it means
processTemplateToFile now takes an explicit intendedOutputDir and enforces containment: the resolved absolute target path must start with the intended output directory before anything is written. If a supporting file, model/api file, or doc file computes to a path escaping the output folder (via '..' segments or an absolute path), generation aborts with both paths printed. This is a deliberate path-traversal containment check (added as hardening), so it fires on generator/template configurations - or spec-sourced names - that produce out-of-tree output paths.
Source
Thrown at modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java:1470
protected File processTemplateToFile(Map<String, Object> templateData, String templateName, String outputFilename, boolean shouldGenerate, String skippedByOption) throws IOException {
return processTemplateToFile(templateData, templateName, outputFilename, shouldGenerate, skippedByOption, this.config.getOutputDir());
}
/**
* Stores lowercased absolute paths for O(1) case-insensitive duplicate detection.
*/
private final Set<String> seenFilesLower = new HashSet<>();
private File processTemplateToFile(Map<String, Object> templateData, String templateName, String outputFilename, boolean shouldGenerate, String skippedByOption, String intendedOutputDir) throws IOException {
String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar);
File target = new File(adjustedOutputFilename);
if (ignoreProcessor.allowsFile(target)) {
if (shouldGenerate) {
Path outDir = java.nio.file.Paths.get(intendedOutputDir).toAbsolutePath();
Path absoluteTarget = target.toPath().toAbsolutePath();
if (!absoluteTarget.startsWith(outDir)) {
throw new RuntimeException(String.format(Locale.ROOT, "Target files must be generated within the output directory; absoluteTarget=%s outDir=%s", absoluteTarget, outDir));
}
// O(1) case-insensitive duplicate check via a pre-lowercased shadow set
if (!seenFilesLower.add(absoluteTarget.toString().toLowerCase(Locale.ROOT))) {
LOGGER.warn("Duplicate file path detected. Not all operating systems can handle case sensitive file paths. path={}", absoluteTarget);
}
return this.templateProcessor.write(templateData, templateName, target);
} else {
this.templateProcessor.skip(target.toPath(), String.format(Locale.ROOT, "Skipped by %s options supplied by user.", skippedByOption));
return null;
}
} else {
this.templateProcessor.ignore(target.toPath(), "Ignored by rule in ignore file.");
return null;
}
}
public Map<String, List<CodegenOperation>> processPaths(Paths paths) {View on GitHub (pinned to fcec517be3)
Solutions
- Compare the two printed paths: absoluteTarget shows exactly which file escaped; trace it back to the SupportingFile destination or template filename that produced it.
- Remove '..' segments and leading separators from supporting-file destinations and template filename expressions; keep every destination relative to outputFolder.
- If spec strings (schema ids, operation ids, tag names) feed filenames, sanitize them in the generator (strip '/', '\\', '..') or fix the offending names in the spec.
- If you genuinely need files elsewhere, generate into a parent output folder that contains the desired location, then relocate with a build step - do not fight the containment check.
- Set outputFolder as an absolute path to avoid relative-vs-absolute divergence between the two compared paths.
Example fix
// before (custom generator)
supportingFiles().add(new SupportingFile("index.html", "../../docs", "index.html"));
// after - relative destination inside the output folder, relocate later if needed
supportingFiles().add(new SupportingFile("index.html", "docs", "index.html")); Defensive patterns
Strategy: validation
Validate before calling
// Contain every computed output path before generation (mirrors the library check)
Path outDir = Path.of(config.outputFolder()).toAbsolutePath().normalize();
List<Path> targets = new ArrayList<>();
for (SupportingFile sf : config.supportingFiles()) {
String f = (sf.destinationFolder == null ? "" : sf.destinationFolder + "/") + sf.destinationFilename;
Path t = outDir.resolve(f).normalize();
if (!t.startsWith(outDir) || f.contains("..")) {
throw new IllegalStateException("Supporting file escapes output dir: " + f);
}
targets.add(t);
} Type guard
private static boolean isContainedPath(Path outDir, String relativeName) {
if (relativeName.startsWith("/") || relativeName.contains("..")) return false;
return outDir.resolve(relativeName).normalize().startsWith(outDir.normalize());
} Try / catch
try {
generator.opts(input).generate();
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Target files must be generated within the output directory")) {
// parse absoluteTarget=... and outDir=... from the message;
// fix the supporting-file destination / template filename, never whitelist the escape
throw new GenerationFailure("Output path containment violated", e);
}
throw e;
} Prevention
- Never use '..' or absolute paths in SupportingFile destinations or template filename expressions.
- Sanitize any spec-derived string (schema/operation/tag names) before it enters a filename.
- Configure outputFolder as an absolute path so generator and check compare the same base.
When it happens
Trigger: A SupportingFile destination containing '..' ('new SupportingFile("index.html", "../docs", ...)'); a custom generator computing output filenames from spec content (schema/operation names) that embed '/' or '..'; a template whose filename expression yields an absolute path; outputFolder configured relative while the computed target is absolute (e.g. '/tmp/x' or 'C:\\x'), so toAbsolutePath() diverges from the output dir.
Common situations: Custom generators written before the containment check that deliberately wrote outside the output folder (e.g. placing docs next to sources); specs with names containing slashes injected into file paths; Windows absolute paths (C:\\) interacting with a mismatched outputFolder; template filename lambdas using un-sanitized spec strings.
Related errors
- Can't instantiate config class with name '{name}'. The class
- Failed to generate .openapi-generator-ignore when the option
- Could not generate supporting file '{ignoreFileNameTarget}'
- Could not generate supporting file '{versionMetadata}'
- Template location must be constrained to template directory.
AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22).
Data as JSON: /api/errors/69510d5b4c347cca.
Report an issue: GitHub.