SonarSource/sonarqube · error · MessageException

a JVM option can't overwrite mandatory JVM options.

Error message

a JVM option can't overwrite mandatory JVM options. %s overwrites %s

What it means

The single-value variant of checkMandatoryOptionOverwrite validates one user-supplied JVM option and throws a MessageException if it would overwrite a mandatory option SonarQube force-sets on the child JVM. The message names both the offending value and the mandatory option (key+value) it would replace.

Solutions

  1. Delete the colliding option from the user JVM options property
  2. Let SonarQube control that setting through its dedicated property (e.g. heap-size properties for Elasticsearch)
  3. Read 'X overwrites Y' in the message to see the reserved option (Y) and remove user option (X)
  4. Re-check the property after SonarQube upgrades, as the set of mandatory options can grow

Example fix

// before
sonar.web.javaOpts=-Xmx1024m -Xms512m
// after (non-reserved flags only)
sonar.web.javaOpts=-XX:+HeapDumpOnOutOfMemoryError
Defensive patterns

Strategy: validation

Validate before calling

boolean conflictsWithMandatory(String opt, Map<String,String> mandatory) {
  return mandatory.keySet().stream().anyMatch(k -> opt.startsWith(k));
}

Try / catch

try {
  launcher.start();
} catch (MessageException e) {
  if (e.getMessage().contains("overwrites")) {
    log.error("User JVM option conflicts with a mandatory option", e);
  }
}

Prevention

When it happens

Trigger: Calling addFromMandatoryProperty with a single JVM option string whose key matches a reserved mandatory option, e.g. passing -Xmx or -Xms where the process heap is managed by SonarQube itself.

Common situations: Hand-tuning child process heap or GC flags that newer SonarQube versions reserve, applying the same javaOpts template to all processes when one process has that option reserved.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/d77dc675875aae6b. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-main/src/main/java/org/sonar/application/command/JvmOptions.java:129

   *
   * @throws IllegalArgumentException if argument is empty or does not start with {@code -}.
   */
  public T add(String str) {
    requireNonNull(str, JVM_OPTION_NOT_NULL_ERROR_MESSAGE);
    String value = str.trim();
    if (isInvalidOption(value)) {
      throw new IllegalArgumentException("a JVM option can't be empty and must start with '-'");
    }
    checkMandatoryOptionOverwrite(value);
    options.add(value);

    return castThis();
  }

  private void checkMandatoryOptionOverwrite(String value) {
    Map.Entry<String, String> overriddenMandatoryOption = mandatoryOptionFor(value);
    if (overriddenMandatoryOption != null) {
      throw new MessageException(String.format(
        "a JVM option can't overwrite mandatory JVM options. %s overwrites %s",
        value,
        overriddenMandatoryOption.getKey() + overriddenMandatoryOption.getValue()));
    }
  }

  @CheckForNull
  private Map.Entry<String, String> mandatoryOptionFor(String jvmOption) {
    return mandatoryOptions.entrySet().stream()
      .filter(s -> jvmOption.startsWith(s.getKey()) && !jvmOption.equals(s.getKey() + s.getValue()))
      .findFirst()
      .orElse(null);
  }

  private static boolean isInvalidOption(String value) {
    return value.isEmpty() || !value.startsWith("-");
  }

View on GitHub (pinned to 184c821202)