skip to content

In a Gatling simulation, which placeholder syntax reads a value back out of the Session, and what does a request URL still written with `${}` actually send?

level: juniorimportance: must knowfreq 74%

answer

  1. Only one placeholder form is live
  2. Hash brace, never dollar brace
  3. Old form clashed with string interpolation
  4. Removed in 3.11.0, now inert text
  5. Backslash escapes a literal placeholder

basics

~10 s

Gatling interpolates only the #{attributeName} placeholder. The older ${...} form was removed in Gatling 3.11.0 and is no longer interpreted, so a URL containing it is sent to the server as literal characters.

solid answer

~40 s

Gatling's Expression Language has exactly one placeholder form, `#{}`. Writing `.get("/accounts/#{accountId}/orders")` substitutes the Session attribute named `accountId` when the request is built. The `${}` form Gatling used for years was deprecated in 3.7.0 and **removed in 3.11.0**, because it collided with Scala and Kotlin string interpolation; since then Gatling's EL compiler recognises `#{` and nothing else. A URL that still carries `${accountId}` raises no error and logs no warning — Gatling treats it as ordinary static text and sends those characters to the server, which normally surfaces as a 404 or a validation error from the application rather than as a Gatling failure. To emit a literal placeholder, escape it with a backslash: `\#{foo}` renders as `#{foo}`.

code

java · 3 lines
java
exec(http("Order history")
  .get("/accounts/#{accountId}/orders")
  .check(status().is(200)));

go deeper

for a junior

Be ready to write a request path that reads a Session attribute, and to say out loud that the only placeholder Gatling interprets is the hash-brace form.

for a middle

Be ready to explain that interpolation happens only inside Gatling SDK methods, and to walk through the backslash escaping rules for a payload that legitimately contains the placeholder characters.

for a senior

Be ready to diagnose an upgraded suite that still passes while sending literal placeholders, and to say which artefact of the run exposes it — the KO breakdown, not the exit code.

for a principal

Be ready to argue how a team keeps a silent syntax removal from reaching production traffic: a source sweep at upgrade time, and a smoke assertion narrow enough to fail on a wrong path.

Gatling's **Expression Language (EL)** is the templating layer that lets a request refer to data the virtual user is carrying. It has one syntax, `#{}`, and understanding both what it interpolates and where it refuses to is the first thing a Gatling author needs. ## What a placeholder resolves against Every virtual user carries a **Session**: an immutable map of named attributes. Values land in it from a feeder record, from a check that saved an extracted value, or from a programmatic `set` call. EL is how you read one back out: ```java exec(http("Order history") .get("/accounts/#{accountId}/orders") .check(status().is(200))); ``` When that request is built for a given virtual user, Gatling replaces `#{accountId}` with the value of that user's `accountId` attribute. The substitution happens per user, per execution — the compiled expression is reused, the value is not. The reference states one constraint before anything else, and it is the one that trips people: **only Gatling SDK methods interpolate EL Strings.** `#{}` is not a feature of Java, Kotlin, Scala or TypeScript. It is a convention that Gatling's own compiler applies to Strings handed into DSL methods such as `get`, `queryParam`, `formParam`, `header` and `StringBody`. The identical characters inside a String you assemble yourself are just characters. ## The dollar syntax is gone, and it fails quietly | Gatling version | status of `${}` | what a `${attr}` URL does | |---|---|---| | before 3.7.0 | the only syntax | interpolated | | 3.7.0 – 3.10.x | deprecated, `#{}` introduced | interpolated, with a WARN log | | **3.11.0 and later** | **removed** | **sent verbatim as literal text** | From 3.11.0 on, Gatling's EL compiler scans for the two characters `#{` and for nothing else. A leftover `${accountId}` is therefore not an error, not a warning, and not a failed request. It is static text. The request goes out against a path such as `/accounts/${accountId}/orders`, and the first sign of trouble is whatever the application under test does with it — usually a 404, sometimes a 200 on a catch-all route, which is worse because the run looks healthy. That silence is the practical hazard. Two habits defuse it: * **Read the run's KO breakdown, not just its exit status.** A wall of identical 404s on one path is the signature. * **Grep the simulation sources for `${`** when migrating anything written against Gatling 3.10 or earlier; there is no compiler error to catch it for you. ## Why the syntax changed `${}` is also Scala's and Kotlin's own string-interpolation marker. In Kotlin, this is the collision in a single file: ```kotlin val accountId = "hard-coded" // Kotlin interpolates this itself: the local variable is baked in val wrong = "/accounts/${accountId}/orders" // Gatling EL: resolved per virtual user from the Session val right = "/accounts/#{accountId}/orders" ``` A Kotlin or Scala author writing the old form got their own language's interpolation, silently, with no indication that Gatling was never involved. Moving to `#{}` — a sequence neither language claims — removed the ambiguity. Gatling's reference attributes the change to exactly that clash. ## Escaping a literal placeholder Sometimes the payload itself must contain the characters `#{`, for instance when the system under test stores templates of its own. The escape character is a backslash, and doubling it escapes the backslash: | written in the String | rendered result | |---|---| | `#{foo}` | the value of attribute `foo` | | `\#{foo}` | the literal text `#{foo}` | | `\\#{foo}` | a backslash, then the value of `foo` | | `\\\#{foo}` | a backslash, then the literal `#{foo}` | ## One syntax across all five languages The placeholder is the same string in every Gatling SDK — Java, Kotlin, Scala, JavaScript and TypeScript. Gatling's own documentation samples use the identical `"#{username}"` form in each language tab. What differs between SDKs is the surrounding code, never the placeholder. So unlike many Gatling facts, this one needs no per-language qualification. Three things to keep straight: 1. **`#{}` is the only interpolated form** in the 3.15.x line. 2. **A stale `${}` is inert**, not an error — no log, no KO from Gatling itself. 3. **Escaping is by backslash**, and the doubling rules matter when a payload legitimately contains `#{`.

  • How do you send the literal characters `#{orderId}` in a Gatling request body without Gatling resolving them?
    Prefix the placeholder with a backslash: `"\#{orderId}"` renders as `#{orderId}`. Doubling the backslash escapes the backslash instead, so `"\\#{orderId}"` emits one backslash followed by the resolved value, and `"\\\#{orderId}"` emits a backslash followed by the literal placeholder.
  • Does the `#{}` syntax differ between Gatling's Java, Kotlin, Scala and JavaScript SDKs?
    No. It is a string convention interpreted by the Gatling engine, not by the host language, and Gatling's documentation samples use the identical form in every language tab. Only the code around the string changes — for example Scala's `session("key").as[String]` versus the Java API's `session.getString("key")` when you drop out of EL entirely.
  • Why does a simulation upgraded from Gatling 3.10 to 3.11 keep passing while sending wrong URLs?
    Because the removal made `${}` inert rather than invalid. Nothing fails to compile and nothing logs, so requests go out with the placeholder text in the path. The failure only appears as application-level errors — often 404s concentrated on one path — so it is found by reading the KO breakdown, not the exit code.

It is a find-and-replace pass that Gatling runs over strings you hand its own methods, not a feature of the programming language, so text it no longer recognises simply travels through untouched.

saying these in an interview costs you the question

  • Believing Gatling still accepts ${attr} as a fallback placeholder form
  • Expecting a warning or a failed request when ${attr} survives an upgrade
  • Thinking the dollar form was dropped for speed rather than interpolation clashes
  • Assuming #{} is a Java or Scala language feature rather than Gatling's own convention
  • Expecting #{} to be interpolated inside a string your own code builds