skip to content

Since Gatling 3.13 a run needs the JVM option --add-opens=java.base/java.lang=ALL-UNNAMED - when do you have to supply it yourself?

level: seniorimportance: nice to knowfreq 22%

answer

  1. A 3.13 requirement, not an older one
  2. Plugins set it for you from specific versions
  3. Report generation needs java.base/java.lang open
  4. Assigning Gradle jvmArgs replaces the defaults

basics

~20 s

Whenever the JVM is not launched by a recent Gatling plugin. gatling-maven-plugin 4.11.0, gatling-gradle-plugin 3.13.1 and gatling-sbt 4.10.2 set it by default; supply it yourself for a hand-rolled java command, an IDE run configuration, or a replaced jvmArgs list.

solid answer

~40 s

Gatling 3.13 refactored HTML report generation, and the new implementation needs `--add-opens=java.base/java.lang=ALL-UNNAMED` on the JVM running the simulation. The build-tool plugins add it for you from gatling-maven-plugin 4.11.0, gatling-gradle-plugin 3.13.1 and gatling-sbt 4.10.2, so a normal `mvn gatling:test` or `./gradlew gatlingRun` needs nothing. You supply it manually in three cases: launching `io.gatling.app.Gatling` yourself from a `java` command; running a simulation from an IDE run configuration, where Gatling's debugging guide tells you to enter it in the VM options field; and — the one that catches people — setting `jvmArgs` in the Gradle `gatling` extension, because assigning that list **replaces** the plugin's defaults, both `--add-opens` entries included.

code

bash · 7 lines
bash
# --results-folder has no default, and the file DataWriter is on by default,
# so the run throws on startup without it
java --add-opens=java.base/java.lang=ALL-UNNAMED \
  -cp "target/test-classes:target/classes:$(cat target/cp.txt)" \
  io.gatling.app.Gatling \
  --simulation com.project.simu.CheckoutSimulation \
  --results-folder target/gatling

go deeper

for a junior

Know that the option exists and that a normal build-tool run already supplies it, so a plain mvn gatling:test needs nothing extra from you.

for a middle

Explain that Gatling 3.13's report-generation refactor needs reflective access into java.base/java.lang, and name the plugin versions that add the flag.

for a senior

Spot the replace-versus-append trap when a build overrides jvmArgs, and verify the launched command line instead of assuming which defaults survived.

for a principal

Set the convention that JVM options for load runs are written in one reviewable place, since a silently dropped flag surfaces far from the change that dropped it.

From Gatling 3.13, running a simulation requires the JVM option `--add-opens=java.base/java.lang=ALL-UNNAMED`. The reason is prosaic: the HTML report generation was refactored, and the new implementation uses techniques that need reflective access into `java.base/java.lang`, which the module system closes by default. ## Reading the option itself `--add-opens=java.base/java.lang=ALL-UNNAMED` is a standard JVM flag with three parts: the module `java.base`, the package `java.lang` inside it, and the target `ALL-UNNAMED`, which means every class loaded from the classpath rather than as a named module. It opens that one package for deep reflective access, and it is a JVM launch flag — it cannot be set from inside the program, from `gatling.conf`, or from a system property. Whatever starts the JVM has to carry it, which is exactly why the question is "who builds the command line" rather than "which Gatling setting turns it on". ## Who supplies it, and who does not Gatling's build-tool plugins set it for you from specific versions: | Plugin | Sets `--add-opens` by default from | |---|---| | `gatling-maven-plugin` | 4.11.0 | | `gatling-gradle-plugin` | 3.13.1 | | `gatling-sbt` | 4.10.2 | So an ordinary `mvn gatling:test` or `./gradlew gatlingRun` on a current plugin needs nothing from you. You have to supply it yourself in three situations. ### 1. You launch the JVM yourself If a script runs `io.gatling.app.Gatling` directly — a container image, a bespoke wrapper, an in-house runner — nothing is adding JVM options on your behalf. Put the option on the `java` command line. While you are writing that command line, remember that the plugins also supply Gatling's **own** options, and one of them is not optional: `io.gatling.app.Gatling` has no default results directory, and `gatling.data.writers` ships as `[console, file]`, so the file `DataWriter` is enabled. Building the stats engine then throws `IllegalArgumentException("Can't use the file DataWriter without setting the results directory")` before a single request is sent unless the command also passes `--results-folder <dir>` (short `-rf`). Turning the file writer off does not rescue it either — report generation asks for the same directory and throws in its turn. A hand-rolled launcher therefore carries two things the plugin was carrying for it: the `--add-opens` flag and the results directory. ### 2. You run from an IDE An IDE run configuration builds its own command line. Gatling's debugging guide has you open the run configuration's JVM/VM options field and enter `--add-opens=java.base/java.lang=ALL-UNNAMED` there. This is the case people hit while debugging a simulation that runs perfectly from the build tool. ### 3. You replaced the plugin's JVM arguments This is the subtle one, and it is worth spelling out. The Gradle plugin's `gatling` extension has a `jvmArgs` property whose default list is: ``` -server -Xmx1G -XX:+HeapDumpOnOutOfMemoryError -XX:MaxInlineLevel=20 -XX:MaxTrivialSize=12 --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED ``` Setting `jvmArgs` **replaces** those defaults; it does not append to them. So the natural-looking edit "we need more heap, let me set `jvmArgs = ['-Xmx4G']`" quietly deletes both `--add-opens` entries along with everything else. The build still configures cleanly, the simulation still runs, and the failure surfaces later, in report generation or in whatever the missing module access touches — far from the line that caused it. The fix is to write the whole list, defaults included, when you override it. sbt makes this explicit with a helper: `Gatling / javaOptions := overrideDefaultJavaOptions("-Xms1024m", "-Xmx2048m")` overrides only the options you name and keeps the rest of the plugin's defaults. ## The second entry nobody remembers Notice that the Gradle default list contains **two** `--add-opens` entries, not one. The second, `--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED`, is part of the same default set. Only the `java.lang` one is documented as a hard requirement from 3.13, but a build that replaces `jvmArgs` drops both, so when you rewrite the list, rewrite it whole rather than reconstructing it from memory of the upgrade guide alone. The safest habit is to copy the plugin's documented default list and add to it. ## How to check rather than guess - Look at the launched command line. Most build tools can show the forked JVM's arguments, and the option is either there or it is not — this beats reasoning about which defaults survived. - Check the plugin version against the table above before blaming your configuration. A project on an older plugin with Gatling 3.13 or later needs the option added by hand, wherever it launches from. - Remember that the requirement is a **version claim**: it does not apply below 3.13, and asserting that a 3.12 run needs it is as wrong as asserting a 3.15 run does not. ## Why this is worth an interview question It is a small fact, but it exercises three habits worth having: reading an upgrade guide rather than a changelog line, knowing that "the plugin sets it for you" has a version attached, and recognising replace-versus-append semantics in build configuration. The last one generalises far beyond Gatling — any property documented as "setting them replaces the defaults" is a place where a one-line change can silently drop something you never knew was there.

  • Where do you put the option when running a simulation from an IDE?
    In the run configuration's JVM or VM options field. Gatling's debugging guide walks through adding `--add-opens=java.base/java.lang=ALL-UNNAMED` there, which is why a simulation can run fine from the build tool and fail from a hand-made IDE configuration.
  • How do you add a JVM option in sbt without losing Gatling's defaults?
    Use `Gatling / javaOptions := overrideDefaultJavaOptions("-Xms1024m", "-Xmx2048m")`. That helper overrides only the options you name and keeps the rest of the plugin's default JVM options, which is exactly the trap that plain assignment creates in the Gradle extension.

Setting jvmArgs in the Gradle gatling block is a replacement, not an addition - like handing a chef a brand-new shopping list instead of adding one item to the old one. Everything you did not write down is simply gone.

saying these in an interview costs you the question

  • Assuming every Gatling version has always needed the option
  • Setting Gradle jvmArgs to one flag and expecting the defaults to remain
  • Blaming the simulation code for a missing module-access option
  • Believing an old plugin version adds the option automatically