Pre-Processors, Post-Processors, Extractors, and Correlation: Diagnostics, Failure Modes, and Production Practices
Correlation failures often present as 400/401 responses far downstream from the real cause. The diagnostic method is to reconstruct producer response → extractor → variable → Pre-Processor → consumer request state while preserving the first failed JTL, engine log, and target evidence.
Learning objectives
- Detect hard-coded replay tokens and distinguish them from true response-derived state.
- Identify an extractor that runs against the wrong sample/scope.
- Diagnose one token shared across threads through a global property.
- Recognize unresolved/literal variables and stale defaults.
- Find Pre/Post-Processors scoped more broadly than intended.
- Preserve correlation evidence without logging sensitive values.
1. Preserve first-failure evidence
http://127.0.0.1:8000, ≤3 threads, ≤3 loops. Keep the
failed JMX, JTL, jmeter.log, synthetic server-event
log, exact non-secret CLI, and one-thread debug evidence before
changing extractor or processor scope.
2. Diagnostic sequence
Follow the arrows as a causal data flow. The response exists before the extractor can create a variable, and the downstream request is constructed from that thread's own variable state.
flowchart TD E[Preserve JMX + JTL + jmeter.log + target events] --> V[Confirm JMeter/Java/script versions] V --> P[Identify producer response + expected dynamic field] P --> X[Inspect Post-Processor type/scope/expression/default] X --> S[Inspect thread-local variable after producer] S --> R[Inspect Pre-Processor + downstream request construction] R --> T[Compare target rejection/fingerprint evidence] T --> G[Inspect generator CPU/GC/script errors] G --> C[CI/container/distributed state if relevant] C --> F[Smallest correction] F --> N[Small bounded rerun]
3. Failure mode: replaying a recorded token
Broken plan:
Start Session — /session/start
Use Session — /session/use?token=tok-recorded-123&prepared=TOK-RECORDED-123
The Start Session response is ignored. Every thread replays the same historical token. The local fixture rejects it as unknown. Repair by extracting the current response token and consuming the per-thread variable.
4. Intentionally broken example: extractor attached to the wrong sample
Move JSON JMESPath Extractor from Start Session under
/noise. The response does not contain
session.token, so CORR_TOKEN=CORR_MISSING.
Use Session receives that sentinel and fails.
Evidence chain: Start Session body contains a real synthetic token →
extractor never processes that response → Debug Sampler after
/noise shows sentinel → target Use Session logs
token_fp=missing/unknown → JTL records 401/failed assertion.
Repair only the extractor scope: child of Start Session.
5. Failure mode: reusing one token across threads via property
Broken JSR223 PostProcessor:
props.put('CORR_TOKEN_GLOBAL', vars.get('CORR_TOKEN'))
Consumer:
${__P(CORR_TOKEN_GLOBAL,CORR_MISSING)}
Two threads now race to overwrite one process-wide property. A thread can send another user's token. Even when the server accepts a valid token, the session identity no longer belongs causally to the current virtual user.
Repair by removing the property mutation and reading
${CORR_TOKEN} from the current thread.
6. Failure mode: unresolved variable remains literal
If the request references ${SESSION_TOKEN} but the
extractor creates CORR_TOKEN, JMeter does not magically
map the names. The literal may appear in the outgoing request or an
empty/default path may be used depending on component handling.
Search the JMX for both names, establish one canonical reference name, and use an explicit producer assertion. Do not patch the server to accept the literal.
7. Failure mode: Post-Processor scope is too broad
A Thread-Group-level extractor runs after Start Session, Use
Session, and Noise. After the correct extraction, a later response
without session.token may overwrite the variable with
the default. This can make an unrelated sampler appear to “expire”
the token.
Move the extractor to the producer sampler. Broad scope should be used only when every response in scope legitimately exposes the same contract.
8. Failure mode: PreProcessor scope is too broad
A Thread-Group-level token-preparation PreProcessor runs before
Start Session—when there is no token yet—and before every other
sampler. Its PREP_MISSING state may be confusing in
debug output or accidentally influence samplers that never need it.
Move it under Use Session, the consumer that needs the derived value.
9. Failure mode: logging sensitive correlated values
This is inappropriate for real secrets:
log.info('token=' + vars.get('AUTH_TOKEN'))
jmeter.log is routinely attached to CI artifacts and
support tickets. Log only synthetic values in this lab. In real
testing, log a non-reversible fingerprint or simply correlation
state (present/missing) and thread/label metadata.
10. Correlation failure versus server-side token expiry
A correct extractor can still produce a token that the SUT later rejects because of expiry, single-use semantics, clock skew, or session invalidation. Distinguish:
| Evidence | JMeter correlation defect | SUT token/session behavior |
|---|---|---|
| Producer response | Token missing or parser contract changed. | Token present/valid format. |
| Debug variable | Sentinel/literal/stale/cross-thread value. | Matches producer value. |
| Consumer request | Wrong/missing token. | Correct token sent. |
| Target log | Unknown/malformed token. | Known token but expired/used/revoked. |
11. Correlation cost versus target latency
Extractors and scripts run on the generator around the sample. Expensive parsing can reduce achieved throughput even when HTTP sampler elapsed and target service time remain stable. Compare generator CPU/GC and baseline/with-correlation throughput before attributing a slowdown to the server.
12. Distributed/CI note: variable scope does not cross engines
Thread-local variables live in each worker thread on each JMeter engine. Do not design a remote test that expects one engine's extracted variable to appear on another engine. Shared external state must belong to the SUT or an explicit test-data/control system, not an accidental JMeter property mailbox.
13. Shortcuts to reject
- Do not replace failed extraction with a copied production token.
- Do not store per-user tokens in global properties.
- Do not add retries/long sleeps before proving the correlation chain.
- Do not move extractors to broad scope just to make variables “available everywhere.”
- Do not dump all variables/responses/headers under load to debug one token.
- Do not disable TLS/RMI validation or redirect tests to a public echo service.
- Do not delete the broken JTL/log after the repaired run succeeds.
Knowledge check
What is the smallest repair when Start Session contains the token but the extractor is under /noise?
Move the extractor back under Start Session; do not alter the token value or server contract.
Why is props.put(CORR_TOKEN_GLOBAL, ...) a multi-user bug?
props is process-global, so concurrent threads overwrite/share one value instead of keeping per-user session state.
How do you distinguish missing extraction from server-side expiry?
Compare producer response, extracted thread variable, consumer request, and target token/session evidence in order.
Why should real auth tokens not be written to jmeter.log?
Logs are shared/archived and can expose credentials; record presence/fingerprint rather than the secret.
Can a variable extracted on remote engine A be read directly by a thread on remote engine B?
No. JMeter variables are local to their thread/engine execution context; cross-engine state must be designed explicitly elsewhere.
Official references and version notes
- Elements of a Test Plan — Pre-Processor/Post-Processor purpose, scope, and execution order: configuration → pre-processors → timers → sampler → post-processors → assertions → listeners.
- Component Reference — Regular Expression, JSON/JMESPath, Boundary, JSR223 Pre/Post Processor, User Parameters, and Result Status Action Handler semantics.
- Functions and Variables — thread-local JMeter variables versus process-wide JMeter properties.
- Best Practices — CLI load execution and scripting/performance guidance.
- Apache JMeter downloads — current stable release and Java requirement.
Version-sensitive behavior was rechecked against current Apache
JMeter primary documentation on 2026-09-05. The course baseline
remains Apache JMeter 5.6.3 with a Java 17 JDK
for labs and no third-party plugins; JMeter 5.6.3 requires Java
8+. Pre-Processors execute before their in-scope sampler and
Post-Processors execute after the sampler but before Assertions.
Processor behavior is scope-driven rather than determined by
visual sibling order. Built-in Post-Processor extractors store
results in JMeter variables, which are normally thread-local. JSON
JMESPath Extractor accepts one JMESPath expression, can select a
match, and can set an explicit default when nothing matches.
Regular Expression and Boundary Extractors likewise support
explicit defaults, which are especially useful during debugging so
a missing extraction is distinguishable from a processor that
never ran. JSR223 Pre/Post Processors provide
vars (thread variables), props (shared
JMeter properties), and the relevant sampler/result context;
Groovy with compiled-script caching is preferred over BeanShell
when scripting is actually necessary. Mandatory labs use a
built-in extractor for correlation and only a tiny Groovy
PreProcessor for a deliberately simple transformation; Chapter 10
covers extractor families in greater depth.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.