skip to content

Placeholder Interpolation

Reading a saved attribute back with Gatling's placeholder syntax, the built-ins that make a value on the spot, and the function escape hatch. The older dollar syntax is gone and is not interpreted.

on this pageshow

explore

questions

5

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
open as a page

Which values can a Gatling Expression Language string produce on its own with no Session attribute behind it, and how many values does `#{randomUuid()}` yield when it appears twice in one URL?

level: middleimportance: must knowfreq 52%

basics

~20 s

Gatling EL ships generator functions that need no Session attribute: currentTimeMillis(), currentDate(pattern), randomUuid(), randomSecureUuid(), randomInt(), randomLong(), randomDouble() and randomAlphanumeric(). Each occurrence is evaluated separately, so two randomUuid() calls in one URL give two different ids.

open as a page

In Gatling's Expression Language, what do `#{ids(0)}`, `#{order.total}` and `#{ids.size()}` each read, and what may appear inside the index parentheses?

level: middleimportance: should knowfreq 42%

basics

~20 s

#{ids(0)} takes an element by index, #{order.total} takes a key or field, and #{ids.size()} gives a collection's length. The index may be a literal, a negative offset from the end, or the name of another Session attribute.

open as a page

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?

level: seniorimportance: should knowfreq 36%

basics

~10 s

Only 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.

open as a page

In Gatling's Expression Language, which forms turn an absent or null Session attribute into a value instead of failing the request, and what does each one produce?

level: seniorimportance: nice to knowfreq 26%

basics

~10 s

A bare placeholder on a missing attribute fails, so Gatling never builds the request. #{x.exists()} and #{x.isUndefined()} return booleans instead of failing, and #{x.jsonStringify()} rescues a null value but not an absent one.

open as a page