In JMeter, why can a sampler not use a value that its own Post-Processor extracts?
answer
- A turn has a before half and after
- Extraction sits in the after half
- Tree position does not rescue you
- Look at the request pane, not the response
basics
~20 sPost-Processors run after the sampler. Every reference in the request was resolved and the request was sent before the extraction happened, so the earliest consumer of an extracted value is the next sampler the thread reaches.
solid answer
~40 sExtraction is retrospective. The execution order puts the **Sampler** at step 3 and **Post-Processors** at step 4, so by the time an extractor runs, the request has already been assembled, had every `${...}` resolved, gone out and come back. A value pulled from that response therefore belongs to the *next* sampler, not to the one that produced it. Moving the extractor higher in the tree changes nothing — tree position orders elements only within one type. One detail worth knowing for debugging: a reference to a variable nothing has ever set does **not** render blank. JMeter compiles it to a `SimpleVariable` whose `toString()` returns `"${" + name + "}"`, so the literal text `${token}` goes on the wire. A genuinely empty field means something already wrote an empty value.
code
text · 6 linesThread Group
HTTP Request POST /login
Authorization: Bearer ${token} <- resolved and sent at step 3
JSON Extractor token <- $.access_token <- written at step 4
HTTP Request GET /orders
Authorization: Bearer ${token} <- first request that can carry itgo deeper
Recall that a Post-Processor runs after its sampler, so a correlated value is usable from the following request onwards, never in the request that produced it.
Explain why tree position does not help here, and that an unset reference goes on the wire as literal text rather than as a blank.
Read the request pane rather than the response pane when correlation breaks, and tell an ordering fault apart from a matching fault by whether the field is literal or empty.
Set the plan convention that keeps correlation obvious: where extractors live relative to their consumers, and whether defaults are left unset so failures are loud.
## The mechanism A Post-Processor is, by definition, an element that runs **after** its sampler. The manual's execution order puts the Sampler at step 3 and Post-Processors at step 4, and `JMeterThread.executeSamplePackage(...)` calls `runPostProcessors(...)` only inside the `if (result != null)` block that follows `doSampling(...)`. By the time a Post-Processor runs, the sampler it is attached to has already: 1. had the config elements merged into it, 2. had its Pre-Processors run, 3. served its timers, 4. had every `${...}` reference in its URL, headers and body resolved, 5. gone out on the wire and come back. Step 4 is the one that ends the argument. The request was rendered *before* the extraction existed, so the extraction cannot possibly have contributed to it. The earliest consumer of an extracted value is the **next** sampler the thread reaches. ## The worked example ```text Thread Group HTTP Request POST /login <-- sends Authorization: Bearer ${token} JSON Extractor token <- $.access_token HTTP Request GET /orders <-- this one can use ${token} ``` The `JSON Extractor` under `/login` writes `token` from the login response. The `/login` request itself was already on the wire when that happened. `/orders`, which the thread reaches afterwards, is the first request that can carry the value. ## What the failing request actually sends — not blank This is the detail candidates most often get wrong, and it changes how you debug. A reference to a variable that has **never been set** does not render as an empty string. JMeter compiles a non-function `${token}` to a `SimpleVariable`, whose `toString()` is: ```java if (ret == null) { return "${" + name + "}"; } ``` So the header goes out carrying the literal eight characters `${token}`. In a listener or a JTL you will see the placeholder itself in the request data — which is a much louder signal than a blank field, once you know to look for it. A field is genuinely **empty** only when something has already written an empty value into the variable. The common route is that the extractor did run on an earlier sampler, failed to match, and stored its configured default; in `RegexExtractor.process()` the default is written up front, `if (!defaultValue.isEmpty() || isEmptyDefaultValue())`, before any matching is attempted. So: | What you see in the request | What it tells you | |---|---| | the literal `${token}` | nothing has ever set `token` — wrong ordering, or wrong variable name | | an empty value | something set `token` to empty — the extractor ran and did not match | | a stale, plausible value | the extractor ran on an earlier iteration and has not been refreshed | ## The other trap: User Defined Variables A tempting "fix" is to declare the variable in a User Defined Variables element so it is defined before the request. That does not work, and the component reference says why: *"all the UDV elements in a test plan — no matter where they are — are processed at the start. So you cannot reference variables which are defined as part of a test run, e.g. in a Post-Processor."* The element is evaluated once, at the beginning, and never again. ## How to confirm the ordering rather than guess - Put a **Debug Sampler** immediately after the sampler whose response defines the value; its output shows the thread's variables at that point, so you can see whether the extraction happened. - Look at the *request* pane in **View Results Tree** for the failing sampler: a literal `${token}` in the headers is proof the value did not exist yet, not proof the extractor is broken. - Remember that moving the Post-Processor higher in the tree changes nothing. Tree position orders elements only *within* a type; a Post-Processor drawn above a sampler still runs after it. ## The one-line rule Extraction is retrospective. Whatever a Post-Processor produces belongs to the requests that come after it, never to the request whose own response produced it.
- Would declaring the variable in a User Defined Variables element make it available to that first request?No. The component reference states that every User Defined Variables element in a plan is processed at the start, wherever it sits, and that you therefore cannot reference variables defined during the run, such as by a Post-Processor. The element is evaluated once, before any sampler runs.
- The request goes out with a blank value rather than the literal reference. What does that tell you?That something did write the variable, and wrote it empty. The usual cause is an extractor that ran on an earlier sampler, failed to match, and stored its configured default value. An unset variable would render as the literal reference instead, so a blank is evidence the extraction happened and produced nothing.
It is like printing the tracking number on a parcel before the courier has issued one. The label is composed and stuck down at the counter; the number only exists once the parcel has been handed over. Whatever you learn at hand-over can go on the next parcel, never on this one.
saying these in an interview costs you the question
- Thinks moving the extractor above the sampler makes it run first
- Assumes an unset variable reference renders as an empty string
- Says a User Defined Variables element can hold a value extracted at run time
- Treats a blank field and a literal reference as the same symptom
- Believes the sampler re-sends itself once the variable exists