OpenAPITools/openapi-generator · error · MojoExecutionException

Failed to write collapsed spec {0}

Error message

Failed to write collapsed spec {0}

What it means

Thrown by ValidateMojo when <collapsedSpec> is configured: the resolved spec is serialized and written to {project.build.outputDirectory}/<collapsedSpec>.json|.yaml (normally target/classes), and an IOException — typically because target/classes does not exist yet, is unwritable, or the path is blocked — is wrapped in a MojoExecutionException whose message embeds the target path.

Source

Thrown at modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/ValidateMojo.java:468

    final var parseOptions = new ParseOptions();
    parseOptions.setResolve(true);
    final List<AuthorizationValue> authorizationValues = AuthParser.parse(this.auth);

    final var openApiMerged = new OpenAPIResolver(
        new OpenAPIV3Parser().readLocation(inputFile, authorizationValues, parseOptions).getOpenAPI()).resolve();

    // Switch based on JSON or YAML.
    final var extension = inputFile.toLowerCase(Locale.ROOT).endsWith(".json") ? ".json" : ".yaml";
    final var mapper = inputFile.toLowerCase(Locale.ROOT).endsWith(".json") ? Json.mapper() : Yaml.mapper();

    // Write the merged spec to the output file.
    final var collapsedSpecPath =
        Paths.get(project.getBuild().getOutputDirectory(), collapsedSpec + extension).toAbsolutePath();
    try {
      final var openApiString = mapper.writeValueAsString(openApiMerged);
      FileUtils.writeStringToFile(collapsedSpecPath.toFile(), openApiString, StandardCharsets.UTF_8);
    } catch (final IOException e) {
      throw new MojoExecutionException(
          new MessageFormat("Failed to write collapsed spec {0}", Locale.ROOT).format(collapsedSpecPath), e);
    }

    // Return the path to the collapsed spec file.
    return collapsedSpecPath;
  }

  private void logInvalid(Invalid invalid) {
    LOGLEVEL loglevel = invalid.getSeverity() == Severity.ERROR ? LOGLEVEL.ERROR : LOGLEVEL.WARNING;
    loglevel.logColored(getLog(), invalid.getMessage());
    loglevel.log(getLog(), MessageUtils.buffer().format("Based on rule: %s", invalid.getRule()).toString());
    loglevel.log(getLog(), MessageUtils.buffer()
        .format(isNotEmpty(invalid.getDetails()) ? "Details: %s" : "No details available.", invalid.getDetails())
        .toString());
    loglevel.logColored(getLog(), "----------");
  }

  private enum LOGLEVEL {

View on GitHub (pinned to fcec517be3)

Solutions

  1. Pre-create the directory: bind the goal after a phase that creates target/classes (e.g. process-classes) or add a mkdir step/antrun execution beforehand.
  2. Check the path from the error message for a conflicting directory or lock and remove it.
  3. Ensure the build user can write to ${project.build.outputDirectory} (permissions, volume mount mode).
  4. If writing the collapsed spec during validation is unnecessary, remove <collapsedSpec>.

Example fix

<!-- before: bound to validate phase on clean checkout, target/classes missing -->
<execution>
  <phase>validate</phase>
  <goals><goal>validate</goal></goals>
  <configuration><collapsedSpec>merged</collapsedSpec></configuration>
</execution>

<!-- after: bind later so target/classes exists -->
<execution>
  <phase>process-classes</phase>
  <goals><goal>validate</goal></goals>
  <configuration><collapsedSpec>merged</collapsedSpec></configuration>
</execution>
Defensive patterns

Strategy: validation

Validate before calling

# create target/classes before validating with collapsedSpec
mkdir -p target/classes && mvn openapi-generator:validate

Prevention

When it happens

Trigger: Validate goal with <collapsedSpec>merged</collapsedSpec> running in a phase before target/classes exists (validate/init phases on a fresh checkout); read-only or containerized output directory; target path occupied by a directory; file locked by another process during the write.

Common situations: Binding validate+collapse to an early lifecycle phase so there is nothing in target/ yet; CI with cached/partially restored target directories; Docker volumes mounted read-only; IDE or antivirus locks on Windows.

Related errors


AI-assisted analysis of OpenAPITools/openapi-generator@fcec517be3 (2026-08-22). Data as JSON: /api/errors/875c0bac5982f8d8. Report an issue: GitHub.