skip to content

Why does ${signingKey} inside a JMeter JSR223 element's Script File reach Groovy unresolved?

level: middleimportance: should knowfreq 55%

answer

  1. Only one code field is a property
  2. The path is substituted, the contents are not
  3. The engine sees the file byte for byte
  4. Placeholder syntax is plan syntax

basics

~10 s

JMeter substitutes variable and function references only in the inline Script field. A Script File is read straight off disk and handed to the engine byte for byte, so ${signingKey} arrives as literal text.

solid answer

~60 s

A JSR223 element has two sources of code and JMeter treats them differently. The inline **Script** text area is an element property, so JMeter resolves `${...}` variable and function references in it before the engine ever sees it. The **Script File** field holds only a path: `processFileOrScript` opens that file and passes the reader to the engine, with no substitution step anywhere in between. The manual states it directly — *"Variable and function references in script files will be passed verbatim to the interpreter, which is likely to cause a syntax error."* In Groovy a bare `${signingKey}` outside a string still compiles — `$` is a legal identifier, so the parser sees a call to a method named `$` with a closure argument — and fails at run time with `groovy.lang.MissingMethodException: No signature of method: $`; inside a double-quoted string Groovy reads it as its own interpolation of a Groovy variable that does not exist. The fix is to read the value through the objects JMeter binds into the script's scope rather than through `${}` syntax.

code

groovy · 7 lines
groovy
// sign.groovy - referenced from the Script File field

// WRONG: JMeter never substitutes into a script file's contents
def key = "${signingKey}"

// RIGHT: read the value out of the bound context at run time
def key = vars.get("signingKey")

go deeper

for a junior

Remember the one-line rule: JMeter fills in ${...} in the inline Script box, never inside a script file. Move the lookup into the script instead.

for a middle

Explain why: the inline script is an element property and goes through replacement, while a file's contents are opened and passed to the engine untouched.

for a senior

Know the three failure shapes - a runtime missing-method error on $ for a bare ${...}, a runtime missing-property error inside double quotes, and a silently literal single-quoted placeholder - and which one leaves a run looking healthy.

for a principal

Make it a standard rather than a lesson each engineer learns twice: script files take input through bindings and parameters, and reviews reject ${} inside them.

## Two sources of code, two different rules Every JSR223 element can get its code from one of two places, and only one of them is a JMeter property: - **Script** (the inline text area) is stored as `<stringProp name="script">` in the plan. Like any other element property, it is passed through JMeter's variable and function replacement before the value reaches the bean, so `${signingKey}` and `${__UUID()}` are already text by the time the engine is handed the source. - **Script File** is stored as `<stringProp name="filename">`. That *path* is a property and is substituted; the **contents of the file are not**. JMeter opens the file with a plain reader and gives it to the engine directly. So `${signingKey}` inside a `.groovy` file is exactly what the engine receives. Nothing in JMeter ever looks at it. ## What Groovy makes of it The manual's phrasing is *"likely to cause a syntax error"*, and how true that is depends on where you wrote it: 1. Outside a string — `def key = ${signingKey}` — Groovy still compiles it: `$` is a legal identifier, so the parser reads a call to a method named `$` with a closure argument. It fails at run time instead, with `groovy.lang.MissingMethodException: No signature of method: $`, which surfaces as a `ScriptException`. Because the text compiles, Tools > Compile JSR223 Test Elements passes it. 2. Inside a double-quoted string — `def key = "${signingKey}"` — the text is valid Groovy, but it is now *Groovy's* interpolation, resolved against Groovy's own scope. There is no local or bound variable of that name, so it fails at run time too, with a `MissingPropertyException` naming it. 3. Inside a single-quoted string — `def key = '${signingKey}'` — it is a plain literal and your signature is computed from the literal placeholder text itself. This is the nastiest of the three, because nothing fails. Case 3 is why this bug survives review: a run that signs every request with the literal string `${signingKey}` produces requests and results, just wrong ones. ## The fix Do not put JMeter syntax in a script file. Read the value at run time from the objects JMeter binds into the script's scope — the bound-context objects are their own topic, but the shape is `vars.get("signingKey")` for a variable and `props.get("signing.key")` for a property. The `Parameters` field of the element is the other supported channel: it *is* a property, so `${...}` in it is substituted, and the resulting text reaches the script as a binding. That gives you a script file that is constant text — which is also exactly what the compilation cache wants, since a file is keyed on its path and last-modified time rather than on its content. ## Why the split exists at all Substitution happens when JMeter prepares an element's properties for use. A file's contents are never an element property, so there is no point at which JMeter could substitute into them without reading and rewriting the file on every execution. Keeping files verbatim is what makes them safely compilable once and reusable. ## Symptoms to recognise - A `ScriptException` wrapping `groovy.lang.MissingMethodException: No signature of method: $` at the line where the `${...}` appears — case 1. Note the GUI's Compile JSR223 Test Elements check passes this, because the text compiles. - A `MissingPropertyException` or a null value for something you thought JMeter would fill in — case 2. - A request that is well-formed but always carries the same literal placeholder text where a real value should be — case 3. All three go away the moment the file stops containing `${}` at all, which is the rule worth writing down: **`${}` is plan syntax, not script syntax.**

  • Where in a JMeter JSR223 element does ${...} still work if the code lives in a Script File?
    In the `Parameters` field and in the `Script File` path itself. Both are element properties, so JMeter substitutes them: `${env}/sign.groovy` resolves to a real path, and `${signingKey}` in `Parameters` arrives at the script as substituted text through the parameters binding. Only the file's contents escape substitution.
  • A JMeter JSR223 script file signs requests but every signature is identical and obviously wrong. What do you look for first?
    A `${...}` reference inside single quotes in the file. Outside a string it fails at run time with a missing-method error on `$`, and inside double quotes with a missing-property error, but inside single quotes it is a valid literal, so the script happily signs the placeholder text itself and the run stays green.

saying these in an interview costs you the question

  • Says JMeter substitutes variables everywhere in a plan
  • Claims a script file is preprocessed before compilation
  • Confuses Groovy string interpolation with JMeter's ${} syntax
  • Expects an unresolved reference in a file to come through empty
  • Suggests reading the file manually to substitute the values