In a Gatling Java simulation, why does `queryParam("lat", Integer.parseInt("#{lat}"))` not work, and what do you pass instead when the value needs real computation?
answer
- Only Gatling's own methods interpolate
- Your code runs before Gatling sees it
- A function's return value is not rescanned
- Pass a Session function for computation
- Java Session getters, Scala as and validate
basics
~10 sOnly Gatling's own SDK methods interpolate placeholders. Integer.parseInt runs first, on the literal placeholder characters, and throws. When a parameter needs computing, pass a function of the Session instead of a placeholder string.
solid answer
~40 sGatling's Expression Language is not a language feature — it is a convention that Gatling's SDK methods apply to the Strings you hand them. `Integer.parseInt("#{lat}")` is plain Java: it runs while the simulation is being constructed, on the literal text `#{lat}`, and throws a `NumberFormatException` before Gatling ever sees a placeholder. Wrapping it in a lambda does not help either, because a method that takes a function receives the function's return value as-is and never compiles it as EL. The escape hatch is the **function** overload: in the Java API and Kotlin a `Session -> T`, in Scala an `Expression[T]`, which is `Session => Validation[T]`. Inside it you read the Session through its API — `session.getInt("lat")` in Java — and compute whatever you need.
code
java · 6 lines// EL: plain substitution, compiled by Gatling
exec(http("Nearby").get("/nearby").queryParam("lat", "#{lat}"));
// function: computation Gatling EL cannot express
exec(http("Nearby").get("/nearby")
.queryParam("lat", session -> session.getInt("lat") * 2));go deeper
Be ready to say that a placeholder is only interpreted when the string goes straight into a Gatling method, and to recognise a parse call wrapped around one as a mistake.
Be ready to explain the three overload families — EL string, function, static value — and to write the function form with the right Session getter for the type you need.
Be ready to diagnose an endpoint returning 4xx with placeholder text in the payload, and to trace it to a function returning an EL string rather than a value.
Be ready to set the house rule on where a suite draws the EL-versus-function line, and to defend it on what each one fails at, when it fails, and what a compiler can check.
Gatling offers two ways to make a parameter dynamic, and the mistake in the question comes from mixing them. Knowing where one ends and the other begins is what stops a simulation from failing in its constructor. ## What interpolation actually is Gatling's reference puts the rule first: *only Gatling SDK methods will interpolate Gatling EL Strings.* `#{}` is not understood by Java, Kotlin, Scala or TypeScript. It is a convention that Gatling's own compiler applies to a String **after** that String has been handed to a DSL method such as `get`, `queryParam`, `header` or `StringBody`. So `queryParam("lat", "#{lat}")` works: the String arrives intact at a Gatling method, which compiles it into an expression evaluated per virtual user. And `Integer.parseInt("#{lat}")` does not, for a reason that has nothing to do with Gatling. Java evaluates the argument first. `parseInt` is called on the six characters `#{lat}`, cannot parse them as a number, and throws a `NumberFormatException` — while the simulation class is being constructed, before any user runs. The same trap has a subtler shape: ```java // also wrong: the method takes a function, so its String result is used as-is queryParam("lat", session -> "#{lat}"); ``` Here the overload receiving a function is chosen, Gatling calls the function per user, and whatever it returns becomes the value. Nothing re-scans that result for placeholders. The request goes out with the literal text `#{lat}`. ## The three overload families | what you pass | how Gatling treats it | |---|---| | a `String` | compiled as an EL expression, evaluated per virtual user | | a function of the `Session` | called per virtual user; the returned value is used verbatim | | any other object | a static value, fixed for the whole run | The third row matters more than it looks. Because `queryParam(String, Object)` exists, a value that is not a String is simply pinned — which is exactly what you want for a constant and exactly what you do not want if you thought a placeholder was hiding inside it. ## The function escape hatch When a value needs arithmetic, a conditional, a lookup or a format EL cannot express, pass a function: * **Java and Kotlin:** `Session -> T`, that is `java.util.function.Function<Session, T>`. * **Scala:** `Expression[T]`, an alias for `Session => Validation[T]`; plain values are lifted into `Validation` implicitly. * **JavaScript and TypeScript:** Gatling's own samples show the same shape, `(session) => ...`. Inside the function you read the Session through its API rather than through EL: ```java exec(http("Nearby") .get("/nearby") .queryParam("lat", session -> session.getInt("lat") * 2)); ``` The Java API offers typed getters — `getString`, `getInt`, `getLong`, `getDouble`, `getBoolean`, `getList`, `getSet`, `getMap`, plus boxed variants and an untyped `get`. Scala uses `session("lat").as[Int]`, `asOption[Int]` when absence is legal, and `validate[Int]` when you want a failure rather than an exception. Gatling's JavaScript sample shows an untyped `session.get("key")`. Two constraints the reference is explicit about: these functions run on Gatling's shared threads, so a long blocking call inside one stalls other virtual users; and Gatling SDK components have no effect when constructed inside them. ## Choosing between the two | | EL string | function | |---|---|---| | checked by the compiler | no | yes | | fails when | the expression resolves, per request | the same, but as ordinary code | | renaming an attribute | silent breakage | still silent — the key is a String either way | | expresses conditionals, arithmetic, formatting | no | yes | | reads at a glance | very well | less well | The working rule most suites settle on: **EL for substitution, functions for computation.** A placeholder that is doing nothing but dropping a saved value into a path is clearer than a lambda doing the same thing. The moment a branch, a cast or a calculation appears, the function is not just permitted, it is the maintainable choice — and it is the only one the compiler can check. ## How this fails in practice The `parseInt` shape fails loudly and early, in the constructor, which is the good case. The lambda-returning-a-placeholder shape fails quietly: every request carries the literal text and the server rejects it, so the run looks like an application problem rather than a scripting one. When a whole endpoint is returning 4xx with placeholder text visible in the error message, look for a function that is returning an EL string instead of a value.
- Why does `queryParam("lat", session -> "#{lat}")` send the literal placeholder text?Because the overload taking a function uses whatever the function returns as the value. Gatling compiles EL only for Strings passed directly to the DSL method; a function's result is never re-scanned. Either pass the String `"#{lat}"` directly, or have the function return the value itself with `session.getString("lat")`.
- What is the Scala signature of one of these functions, and why is it not just `Session => T`?It is `Expression[T]`, an alias for `Session => Validation[T]`. The `Validation` wrapper lets a missing or wrong-typed attribute be reported as a failure Gatling can log and record — `Failed to build request <name>: <reason>`, one row in the run's Errors table — instead of an exception thrown out of a shared thread. Plain values are lifted into `Validation` implicitly, so simple functions still read naturally.
- When should a value stay an EL string rather than becoming a function?When it is pure substitution. A placeholder dropping a saved attribute into a path or a header is shorter, reads at a glance, and needs no lambda ceremony. Move to a function as soon as the value needs arithmetic, a branch, a cast or formatting that EL's built-ins cannot express.
The placeholder is a note Gatling reads at the door. Anything your own code does to the string before it reaches that door happens to the ink, not to the message.
saying these in an interview costs you the question
- Thinking a placeholder works anywhere a string appears in the simulation
- Expecting a function's returned string to be interpolated as EL
- Blaming Gatling for a NumberFormatException thrown by your own parse call
- Assuming the EL failure happens at run time rather than in the constructor
- Treating EL and functions as interchangeable rather than substitution versus computation