Troubleshooting & Debugging
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. |
The reference material consulted while preparing these pages is listed in the section bibliography.
A test plan fails in two ways: it does something other than what you meant (the wrong request, an empty variable, a
correlation that never matched), or the run itself breaks (the JVM runs out of memory, the generator runs out of
ports, a remote engine refuses the connection, a plugin is missing). This page covers both. The first half is the
toolbox for debugging a plan: Validate, the Debug Sampler and Debug PostProcessor, the View Results Tree renderers and
testers, jmeter.log, per-package log levels and HTTP wire logging. The second half is a catalogue of the failures that
come up again and again, each as symptom, cause, fix, with the exact message where it was reproduced and a link to
the page of this section that covers the fix in depth.
The page was written against Apache JMeter 5.6.3, the latest release at the time of writing, run on Java 21 (5.6.x
needs Java 8 or later; this guide recommends 17 or later). JMeter 6.0 is not released; where its planned changes (from
xdocs/changes.xml on the master branch) affect troubleshooting, the text says "planned". The plugin-related entries
assume the JMeter Plugins Manager 2.0 and the gRPC Request plugin 2.0.2 (jmeter-grpc-request), and Maven builds the
jmeter-maven-plugin 3.8.x. The error messages quoted below were reproduced on a JMeter 5.6.3 installation with Java 21
on macOS, against local endpoints only, or read from the 5.6.3 and plugin sources; where a message depends on the
operating system or on the server, the text describes it instead of quoting it.
The method follows the troubleshooting chapter of Pro Apache JMeter (ch. 8, JMeter 3.0): read the log first, make
the failure reproducible with one thread, then change one thing at a time. Its tools have changed since then: logging
moved from jmeter.properties to Log4j 2 in 3.2, distributed testing uses RMI over SSL by default since 4.0, and
plugins are installed with the Plugins Manager. The differences are listed at the end of the page.
Where a failure shows up
The first question is not "what is wrong" but "where does it show": the place narrows the cause down more than the message does.
Three rules apply everywhere:
-
Read
jmeter.logbefore anything else. JMeter does not interrupt a test with dialogs. The Getting Started guide says it writes errors to the log file instead, and that it reports no error for a misspelt variable or function: the reference is used as is. -
Reproduce with one thread. A failure that happens with one thread is a plan or data problem; one that appears only under load is a capacity or generator problem. Use Validate in the GUI or a one-thread CLI run (below).
-
Change one thing at a time, and keep the failing JTL and log of each attempt next to each other.
Debugging a plan
Validate, and the one-thread CLI run
Validate (right-click a Thread Group, Validate) runs that Thread Group with one thread, one iteration, no timers
and no Backend Listener, so a journey that takes minutes under its real settings runs in seconds. The properties
behind it (testplan_validation.*) and a worked timing example are on
GUI & Test Plan Workflow.
Validate exists only in the GUI. When the problem appears in CLI runs (a CI agent, a container, a distributed node),
reproduce it in CLI mode with the run properties the Bookshelf plan already reads (threads, rampup, duration),
a log of its own and DEBUG for the package under suspicion:
jmeter -n -t bookshelf-load.jmx -l smoke.jtl -j smoke.log -f \
-Jthreads=1 -Jrampup=1 -Jduration=30 \
-Lorg.apache.jmeter.protocol.http=DEBUG
Creating summariser <summary>
Created the tree successfully using bookshelf-load.jmx
Starting standalone test @ 2026 Oct 14 10:00:00 CEST (1791964800000)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary = 10 in 00:00:30 = 0.3/s Avg: 196 Min: 142 Max: 268 Err: 0 (0.00%)
Tidying up ... @ 2026 Oct 14 10:00:31 CEST (1791964831000)
... end of run
The console lines are those of a 5.6.3 CLI run; the summary figures are illustrative. With the Bookshelf 3 s think time and about 0.2 s per request, one thread makes roughly 1 / 3.2 ≈ 0.31 requests per second, so 30 s give about 10 requests: few enough to read every line of the log, enough to cover the journey twice. Never use these logging settings in a measured run: DEBUG writes several lines per sample and the disk becomes part of the measurement.
Debug Sampler and Debug PostProcessor
Both elements make variables and properties visible in the Response data pane of the View Results Tree. Where to place them and how they help with correlation is on Correlation & Extractors; this section lists their fields with the defaults a new element gets in the 5.6.3 GUI.
Debug Sampler (Add → Sampler → Debug Sampler) produces a sample of its own whose response data lists the current values. It is a sampler, so it appears in the results and counts in the summariser: disable it before a load run.
| Field | JMX property | Default | Meaning |
|---|---|---|---|
Name |
|
Debug Sampler |
Label of the sample. |
JMeter Properties |
|
false |
Include all JMeter properties (several hundred lines). |
JMeter Variables |
|
true |
Include the thread’s variables: what the extractors and CSV files set. |
System Properties |
|
false |
Include the Java system properties: JVM version, |
Debug PostProcessor (Add → Post Processors → Debug PostProcessor) adds a sub-sample to the sampler it is attached to, so the values appear under the request that set them instead of as a separate sample.
| Field | JMX property | Default | Meaning |
|---|---|---|---|
Name |
|
Debug PostProcessor |
Name of the element. |
JMeter Properties |
|
false |
Include JMeter properties. |
JMeter Variables |
|
true in the 5.6.3 GUI (the component reference says false) |
Include JMeter variables. |
Sampler Properties |
|
true |
Include the properties of the parent sampler as JMeter evaluated them: the resolved path, the arguments, the body. |
System Properties |
|
false |
Include Java system properties. |
Sampler Properties is the field that answers "what did JMeter actually send": a ${bookId} that was never set
appears there literally. As a fragment, attached to the Bookshelf login request:
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST /api/auth/login">
<stringProp name="HTTPSampler.path">/api/auth/login</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{"username":"${username}","password":"${password}"}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<DebugPostProcessor guiclass="TestBeanGUI" testclass="DebugPostProcessor" testname="Debug login" enabled="true">
<boolProp name="displayJMeterProperties">false</boolProp>
<boolProp name="displayJMeterVariables">true</boolProp>
<boolProp name="displaySamplerProperties">true</boolProp>
<boolProp name="displaySystemProperties">false</boolProp>
</DebugPostProcessor>
<hashTree/>
</hashTree>
The variables listing includes password and, after the login, token. Treat a View Results Tree or an XML results
file that contains Debug output as a secret, and remove the element (or set enabled="false") before the plan is
committed.
View Results Tree: renderers and testers
The View Results Tree is the debugging listener; its tabs, every renderer and its properties are described on Listeners & Results Files. For troubleshooting, the renderer to pick depends on the question:
| Question | Renderer or tester | How |
|---|---|---|
Did the extractor’s JSONPath match? |
JSON Path Tester |
Select the response, paste the expression of the JSON Extractor (for Bookshelf login, |
Does the JMESPath expression match? |
JSON JMESPath Tester |
Same, with the expression of the JSON JMESPath Extractor. |
Which group does the regular expression capture? |
Regexp Tester |
Shows |
Do the left and right boundaries work? |
Boundary Extractor Tester |
Tests the boundaries the way the Boundary Extractor applies them. |
Does the CSS selector find the element? |
CSS/JQuery Tester |
Choose the JSoup or Jodd implementation to match the extractor; the syntax differs slightly. |
Does the XPath query match a SOAP or XML response? |
XPath Tester, XPath2 Tester |
XPath Tester follows the XPath Extractor; XPath2 Tester (listed in the 5.6.3 |
Is the response JSON or XML at all, and what does it contain? |
JSON, XML |
Tree views; a parse failure here explains an extractor that never matches. |
Is the body empty or binary? |
Text |
Content types starting with |
Two limits mislead people when they debug with the tree. It keeps only the last 500 samples
(view.results.tree.max_results), so the first iterations of a longer debug run disappear; and it does not display
responses above view.results.tree.max_size (10485760 bytes in the 5.6.3 jmeter.properties; the component reference
still mentions 200 KB). For a bigger body, use Save Responses to a file (below). The Request tab shows only the
headers JMeter added, not the ones the HTTP client adds later, such as Host; wire logging shows those. The Browser
renderer is absent on Java 9 and later unless JavaFX is added to the launcher, as the Hints and Tips chapter describes.
Saving failed responses in CLI mode
In a CLI run there is no View Results Tree to look at, and the CSV results file has no bodies. Add → Listener → Save
Responses to a file with Save Failed Responses only writes one file per failed sample: the body the server actually
sent, which is usually the fastest way to the cause of a 500 or a failed assertion. The options to put error bodies
into an XML results file instead are on
Listeners & Results Files.
| Field | JMX property | Use when troubleshooting |
|---|---|---|
Filename Prefix (can include folders) |
|
|
Variable Name containing saved file name |
|
Optional; the file name can then be saved with the sample through |
Minimum Length of sequence number |
|
|
Save Failed Responses only |
|
Tick it. Otherwise every response is written. |
Save Successful Responses only |
|
Off. |
Don’t add number to prefix |
|
Off, or every failure overwrites the same file. |
Don’t add content type suffix |
|
Off: the suffix ( |
Add timestamp |
|
On, to line the file up with server logs (format |
Don’t Save Transaction Controller SampleResult |
|
On, so transaction parents are not saved twice. |
<ResultSaver guiclass="ResultSaverGui" testclass="ResultSaver" testname="Failed responses" enabled="true">
<stringProp name="FileSaver.filename">~/results/failed/resp_</stringProp>
<stringProp name="FileSaver.variablename"></stringProp>
<stringProp name="FileSaver.numberPadLen">0</stringProp>
<boolProp name="FileSaver.errorsonly">true</boolProp>
<boolProp name="FileSaver.successonly">false</boolProp>
<boolProp name="FileSaver.skipautonumber">false</boolProp>
<boolProp name="FileSaver.skipsuffix">false</boolProp>
<boolProp name="FileSaver.addTimstamp">true</boolProp>
<boolProp name="FileSaver.ignoreTC">true</boolProp>
</ResultSaver>
<hashTree/>
It costs nothing while samples succeed and one file write per failure. In a run where everything fails (the server is down) it writes one file per sample, so keep it for smoke and debug runs or watch the disk.
jmeter.log and log levels
Since 3.2 JMeter logs through SLF4J and Log4j 2, configured by log4j2.xml (in bin/, or the directory JMeter is
started from). The log file is jmeter.log in the directory JMeter was launched from, unless -j names another one.
Installation details (the configuration file, the GUI log viewer) are on
Installation & Configuration and the command-line options on
Running Tests in CLI Mode. The tools at a glance:
| Tool | What it does |
|---|---|
|
Writes the run log to another file, one per run. |
|
Sets the root level. Very noisy; rarely what you want. |
|
Sets one package’s level for this run. A category starting with |
|
Uses another Log4j 2 configuration file, for example one kept in the test project. |
Options → Log Viewer |
Shows the log in a pane at the bottom of the GUI. |
Options → Log Level |
Changes the root level of the running GUI without a restart. |
Help → Enable debug / Disable debug |
Switches DEBUG on for the class of the selected element only. Help → What’s this node? shows the GUI and test element class names, to know which category to pass to |
Error counter |
The warning icon at the top right of the GUI counts errors and fatal messages; clicking it opens the log viewer. |
The categories worth knowing, all commented examples in the 5.6.3 log4j2.xml:
| Category | Shows |
|---|---|
|
How the HTTP sampler builds and sends requests, redirects, embedded resources. |
|
Cookie Manager, Authorization Manager, Header Manager and Cache Manager decisions: why a cookie was not sent. |
|
JDBC connection pool and statement handling. |
|
Engine start, thread start and stop, remote engines. |
|
Logic controller decisions. |
|
Dashboard generation. |
|
Requests seen by the HTTP(S) Test Script Recorder. |
|
HttpClient 4 internals, headers and raw bytes: see the next section. |
A log line has the layout date level category: message, with the category shortened (o.a.j.t.JMeterThread).
An exception that stops a thread is logged at ERROR by o.a.j.t.JMeterThread with its stack trace, whose
at lines name the element class that failed (for example CSVDataSet). Groovy scripts write to the same file through their log binding, which is often
the quickest way to print an intermediate value (see
Scripting with JSR223 & Groovy).
HTTP wire logging
When the question is what went over the network (which headers, which body, which status line), the View Results Tree’s Request tab is not enough, because it shows only what JMeter added. The default HTTP implementation, Apache HttpClient 4, logs the real exchange through two categories:
-
org.apache.http.headers: the request and response headers, one line each; -
org.apache.http.wire: every byte, with[\r][\n]for line ends and[0xNN]for non-ASCII bytes. Bodies included.
The shipped log4j2.xml has commented examples for "header wire + context logging" (org.apache.http at DEBUG with
org.apache.http.wire at ERROR) and "full wire + context logging" (org.apache.http at DEBUG). For one run, pass the
categories with -L:
jmeter -n -t bookshelf-load.jmx -l wire.jtl -j wire.log -f \
-Jthreads=1 -Jrampup=1 -Jduration=10 \
-Lorg.apache.http.headers=DEBUG -Lorg.apache.http.wire=DEBUG
grep ' o.a.h.' wire.log | head
The output has this shape (the line format of a 5.6.3 run against a local server; abridged, with the Bookshelf search as the request):
2026-10-14 10:00:47,272 DEBUG o.a.h.headers: http-outgoing-0 >> GET /api/books?query=tolkien HTTP/1.1
2026-10-14 10:00:47,272 DEBUG o.a.h.headers: http-outgoing-0 >> Host: localhost:8080
2026-10-14 10:00:47,272 DEBUG o.a.h.headers: http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.14 (Java/21.0.6)
2026-10-14 10:00:47,272 DEBUG o.a.h.wire: http-outgoing-0 >> "GET /api/books?query=tolkien HTTP/1.1[\r][\n]"
2026-10-14 10:00:47,273 DEBUG o.a.h.wire: http-outgoing-0 >> "[\r][\n]"
2026-10-14 10:00:47,281 DEBUG o.a.h.wire: http-outgoing-0 << "HTTP/1.1 200 [\r][\n]"
2026-10-14 10:00:47,281 DEBUG o.a.h.wire: http-outgoing-0 << "Content-Type: application/json[\r][\n]"
2026-10-14 10:00:47,283 DEBUG o.a.h.headers: http-outgoing-0 << HTTP/1.1 200
>> is sent, << received, and http-outgoing-N identifies the connection, so a new number for every request shows
that connections are not being reused. For connection-pool questions (reuse, stale connections, time_to_live) use
org.apache.http.impl.conn at DEBUG instead.
For TLS, the HTTP logs stop at the socket; the JVM’s own -Djavax.net.debug=ssl:handshake prints the handshake, as
shown on HTTP Requests.
Wire logs contain everything: the Authorization: Bearer header, the login body with the password, cookies and
every response body. Use them with one thread and placeholder credentials, never commit the log, and never enable them
in a load run, where they multiply the disk I/O by the size of every request and response.
Catalogue of common failures
The table is the index; the sections after it give the evidence and the fix, and link to the page that covers the
topic in full. Messages in monospace were reproduced on 5.6.3 or read from the source.
| Symptom | Cause | Fix |
|---|---|---|
|
The heap (1 GiB by default) is too small for the plan: View Results Tree or other GUI listeners in a load run, large responses kept by many threads, too many threads for one JVM |
Remove GUI listeners, write CSV results, raise |
|
The generator ran out of ephemeral ports: a new connection per request leaves thousands of sockets in TIME_WAIT |
Reuse connections, spread the load, widen the port range ( |
|
Another process holds the port (a second recorder, a running |
Stop the other process or choose another port ( |
Samples fail with |
No HTTP response at all: connection refused, unknown host, timeout, TLS failure, or an invalid URL built by the plan |
Read the exception name in the response code ("Non HTTP response code" errors) |
|
TLS against a plain port, a protocol or cipher the server does not accept, or a client certificate the server requires |
Match protocol, port and TLS settings; debug with |
While recording, the browser shows a certificate error on every HTTPS page; |
The browser does not trust the JMeter recorder’s root CA |
Install |
|
|
Start the servers, open the RMI ports, create and copy the keystore (RMI connection refused and keystore errors) |
Summary shows 0 samples; |
The CSV Data Set Config cannot find its file |
Fix the path (relative to the |
A whole CSV line lands in one variable; other variables are empty or keep old values |
Wrong delimiter, header read as data, short lines |
Set the delimiter and Ignore first line correctly (Empty CSV variables and wrong paths) |
|
The variable was never set: misspelt name, extractor did not run, wrong scope, or a property read as a variable |
Find where it should be set, check scope and spelling ( |
Requests carry |
The extractor’s expression did not match, so it wrote its default value |
Test the expression in the View Results Tree, fix it, assert on the default (Extractor defaults hit) |
|
The plugin jar is not in |
Install the plugin with the Plugins Manager or |
Every gRPC sample fails with response code |
The plugin could not compile the protos or resolve the method: bad proto root, missing import, misspelt method |
Fix the proto root, library directory or full method (gRPC "method not found" and proto import errors) |
Every gRPC sample fails with |
The server has no such method: the test’s |
Use the contract of the deployed version (gRPC "method not found" and proto import errors) |
OutOfMemoryError and heap
Symptom. java.lang.OutOfMemoryError with a reason: Java heap space (the heap is full), GC overhead limit
exceeded (the JVM spends nearly all its time collecting), Metaspace (too many classes, typically JSR223 scripts that
are not cached), or unable to create native thread (the operating system refused another thread). Before the error
the summariser shows throughput falling while response times rise for every request at once, which is the generator,
not the server.
Cause. The 5.6.3 launcher starts JMeter with HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" unless HEAP is set,
and the Getting Started guide warns that 1 GB may not be enough for a plan and its thread count. The usual consumers
are listeners that keep results in memory (View Results Tree, View Results in Table, Aggregate Report, the plugin
graphs), large responses held by many threads at once, and XML results with response data.
Fix, in this order:
-
Remove or disable every GUI listener for the load run and write results with
-lto CSV; see Listeners & Results Files. -
Cache JSR223 scripts (no
${...}inside a cached script); see Scripting with JSR223 & Groovy. -
Raise the heap for the run, keeping
-Xmsequal to-Xmxand the total below the machine’s free memory:HEAP="-Xms4g -Xmx4g -XX:MaxMetaspaceSize=256m" \ jmeter -n -t bookshelf-load.jmx -l load.jtl -j load.log -f -
For
unable to create native thread, raise the process and file limits of the user (ulimit -u,ulimit -n) or use fewer threads per generator. -
When one JVM is still not enough, split the load over several generators.
Evidence. The launcher always adds -XX:+HeapDumpOnOutOfMemoryError, so the JVM writes a .hprof file
(named after the process id) into the working directory when the heap is exhausted; open it in a heap analyser to see
which objects filled it. During a run, bin/heapdump.sh asks a CLI run for a dump through the shutdown port without
waiting for the error. Sizing the heap, thread stacks and containers is worked through on
Running Tests in CLI Mode.
java.net.BindException
Two different problems share this exception.
At load, inside samples: Cannot assign requested address (Linux; Windows reports Address already in use:
connect). Every new TCP connection needs a local ephemeral port, and a port stays in TIME_WAIT for 60 s after the
client closes it. A plan that opens a new connection per request exhausts the default Linux range (32768 to 60999,
28,232 ports) at about 470 new connections per second sustained for a minute. The errors look like server failures, but
they appear on the generator, at a constant rate once the threshold is crossed, and the server’s logs show no
corresponding requests. The fix is to keep connections alive (Use KeepAlive, Same user on each iteration), then to
spread the load, then to widen the port range; the arithmetic, the properties and the operating system commands are on
Running Tests in CLI Mode.
At start-up of a listener: Address already in use. The HTTP(S) Test Script Recorder (default port 8888) or a
jmeter-server (registry port 1099, or server_port) cannot open its port because another process already holds it,
usually a previous recorder or server that was not stopped. Find the process (lsof -i :8888 on Linux and macOS,
netstat -ano on Windows), stop it or choose another port. The UDP shutdown port of a CLI run (4445) is the exception:
when it is taken, JMeter tries the next port, up to jmeterengine.nongui.maxport, and prints the one it used.
"Non HTTP response code" errors
When the HTTP sampler gets no HTTP response at all, it records the exception class as the response code, prefixed with
Non HTTP response code:, and the exception message as the response message, prefixed with
Non HTTP response message:. The class name is the diagnosis. These rows were reproduced on 5.6.3 (HttpClient4
implementation, Java 21) against local endpoints:
| Response code and message | Meaning | Fix |
|---|---|---|
|
Nothing listens on that host and port, or a firewall rejected the connection. The message lists every address tried. |
Check |
|
The host name does not resolve on this generator. |
Check the name in HTTP Request Defaults and the |
|
The connection was made but no response arrived within the Response timeout. |
Under load this is a server saturation signal, not a plan bug; see Analyzing Results & Finding Bottlenecks. In a smoke run, the timeout is too low or the endpoint hangs. |
|
Protocol is |
Use |
|
The Path contains a |
|
|
The server closed a kept-alive connection that the client reused. |
Lower |
A row that is the same for every sample from the first second is a configuration problem (address, protocol, URL). A
row that appears only after load has risen (timeouts, refused connections, NoHttpResponseException) is a capacity
signal for the server or a resource limit on the generator. The dashboard’s Errors table groups samples by these
strings; the classification of HTTP status codes is on
Analyzing Results & Finding Bottlenecks.
Retries hide these errors: httpclient4.retrycount defaults to 0 in 5.6.3, and should stay there for measurement.
TLS handshake failures
JMeter’s HTTP samplers accept every server certificate, valid or not, so an untrusted or expired server certificate is not a cause of failure (and a load test does not detect one). What remains:
| Symptom | Cause | Fix |
|---|---|---|
|
TLS against a plain HTTP port (reproduced above). |
Correct Protocol or Port. |
A handshake failure alert from the server (the alert name appears in the message, for example |
No TLS version or cipher suite in common: the server allows only TLS 1.3 and |
Remove or align |
The server rejects the connection after the handshake starts and asks for a certificate |
Mutual TLS: the server requires a client certificate and none was sent. |
Configure the keystore and the Keystore Configuration element, as on HTTP Requests. |
|
The server certificate uses an algorithm the JVM disables (MD2, short keys). |
Fix the certificate in the test environment. |
|
Logged by |
Harmless unless the server needs a client certificate. |
To see the handshake, add -Djavax.net.debug=ssl:handshake to a one-thread run; the JVM prints the protocol versions
and cipher suites offered and the alert that ended the handshake. The TLS properties and the client-certificate setup
are on HTTP Requests. gRPC over TLS has its own settings and fails with
UNAVAILABLE when TLS is on one side only; see gRPC Load Testing.
The recorder unknown_ca error
While recording HTTPS traffic, the browser rejects the certificate the recorder presents for each site and jmeter.log
shows unknown_ca: the browser does not trust the root CA that JMeter generates (ApacheJMeterTemporaryRootCA.crt in
bin/). Install it as a trusted authority in the store the browser actually reads and restart the browser. The
certificate is valid for 7 days by default and is regenerated with proxyserver.jks, so a recording that worked last
week can fail today for the same reason. The full table of certificate symptoms (expiry, dynamic keys, keytool not
found, a browser that refuses outright) is on
Recording & cURL Import.
RMI connection refused and keystore errors
Distributed testing has its own failure modes, all of which happen before the first sample. These were reproduced on 5.6.3 with a client and a server on the same machine:
The server cannot start: no keystore. Since 4.0, RMI uses SSL unless server.rmi.ssl.disable=true, and the
keystore defaults to rmi_keystore.jks (type JKS, alias rmi, password changeit). Without it, jmeter-server stops
at once:
Server failed to start: java.rmi.server.ExportException: Listen failed on port: 0; nested exception is:
java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)
The client cannot connect: no keystore on the client. The client needs the same keystore. Without it, -R fails
for each server and the run exits with status 1:
Configuring remote engine: 127.0.0.1:1299
Exception creating connection to: 127.0.0.1; nested exception is:
java.io.FileNotFoundException: rmi_keystore.jks (No such file or directory)
Failed to configure 127.0.0.1:1299
...
Error in NonGUIDriver java.lang.RuntimeException: Following remote engines could not be configured:[127.0.0.1:1299]
The client cannot connect: nothing listening. With a keystore (or with SSL disabled on both sides), a server that is not running, a wrong port or a firewall gives:
Configuring remote engine: 127.0.0.1:1299
Connection refused to host: 127.0.0.1; nested exception is:
java.net.ConnectException: Connection refused
Failed to configure 127.0.0.1:1299
The fixes, which the distributed testing page of this section covers step by step:
-
Run
bin/create-rmi-keystore.sh(or.bat) once, and copy the samermi_keystore.jksintobin/of the client and of every server, or pointserver.rmi.ssl.keystore.fileat it. Disabling SSL withserver.rmi.ssl.disable=truemust be done on both sides, and only on a network you control. -
Start
jmeter-serveron every node and check it listens on the registry port (1099 by default, orserver_port). -
Fix the ports for firewalls:
server.rmi.localportfor the server engine,client.rmi.localportfor the objects the client exports to receive results (the manual notes that up to three ports from that value are used). With RMI over SSL, a fixedclient.rmi.localportmade every remote start fail in a 5.6.3 test run (Listen failed on port, thenNotSerializableException: ...JMeter$ListenToTest); leave it at 0 in that case, as Distributed Testing explains. -
A server whose host name resolves to a loopback address tells the client to call back to
127.0.0.1; start it with-Djava.rmi.server.hostname=<its reachable address>. -
Use the same JMeter version on every node and, as the manual recommends, the same Java version, plus the same plugins and the same data files.
Empty CSV variables and wrong paths
Missing file. A CSV Data Set Config whose file does not exist does not produce failed samples: the thread stops at
its first iteration, before any request. A 5.6.3 run of a plan whose Filename was data/users.csv with no such file
printed a summary of zero samples, and jmeter.log held the reason:
summary = 0 in 00:00:00 = ******/s Avg: 0 Min: 9223372036854775807 Max: -9223372036854775808 Err: 0 (0.00%)
ERROR o.a.j.t.JMeterThread: Test failed!
java.lang.IllegalArgumentException: File users.csv must exist and be readable
at org.apache.jmeter.services.FileServer.createBufferedReader(FileServer.java:424) ~[ApacheJMeter_core.jar:5.6.3]
...
at org.apache.jmeter.config.CSVDataSet.iterationStart(CSVDataSet.java:182) ~[ApacheJMeter_components.jar:5.6.3]
A zero-sample summary with Err: 0 therefore means "read the log", not "all good". Relative paths are resolved against
the folder of the .jmx, not the working directory, and in a distributed run every server needs its own copy; see
Parameterization & Test Data.
Empty or wrong values. The file is found but the values are wrong. Use a Debug PostProcessor on the first request and compare the variables with the file:
| What the Debug output shows | Cause | Fix |
|---|---|---|
|
The file uses |
Set Delimiter to the file’s separator ( |
|
Variable Names filled in and Ignore first line off on a file with a header. |
Leave Variable Names empty to use the header, or tick Ignore first line. |
|
End of file with Recycle on EOF off and Stop thread on EOF off. |
Recycle, stop the thread, or provide enough rows. |
A variable keeps the value of the previous iteration |
A short line with fewer values than variable names. |
Fix the data; keep files free of blank and short lines. |
Garbled accented terms |
Encoding mismatch. |
Save the file as UTF-8 and set File Encoding to |
${var} not substituted
JMeter does not report an undefined variable or a misspelt function: the Getting Started guide states that the
reference "is just used as is". What happens next depends on where it is used. Both cases were reproduced on 5.6.3 with
a variable term that was never set:
-
In the Path field:
/api/books?query=${term}cannot be turned into a URI, so the sample fails before any connection withNon HTTP response code: java.net.URISyntaxExceptionandIllegal character in query at index 36. -
As a parameter value with URL Encode? ticked: the reference is encoded and sent, as
/api/books?query=%24%7Bterm%7D. The server answers normally (often200with an empty result), so nothing fails unless an assertion checks the content. -
In a JSON body: the literal
${term}is sent; the API either rejects it with400or searches for the text.
The causes, in the order they occur in practice:
| Cause | How to tell, and fix |
|---|---|
Misspelt name or wrong case ( |
Variable names are case-sensitive. Compare with the Debug PostProcessor output. |
The setting element has not run yet |
Execution order: a CSV Data Set Config in a sibling controller, an extractor on a later sampler. Move it; see Test Plan Elements, Execution Order & Scoping. |
The setting element is out of scope |
An extractor under another sampler, or in another Thread Group: variables are per thread. Share across threads with properties; see Properties, Variables & Functions. |
A property read as a variable |
|
The extractor ran but did not match and has no default |
The variable is not created, so the reference stays literal. Give every extractor a default (next section). |
|
The first value is compiled into the script. Use |
|
Evaluated once, by another thread. Use a CSV file, a Counter or a preprocessor. |
To find every element that uses a variable, use the Search menu with the variable name: matching elements are highlighted in the tree.
Extractor defaults hit
When the Bookshelf plan sends Authorization: Bearer TOKEN_NOT_FOUND or calls /api/loans/LOANID_NOT_FOUND, the
extractor ran and did not match. That is the purpose of a loud default value; the alternative is the silent literal of
the previous section. The usual causes:
-
The response is not what you think: an error page, a
401, an HTML login page from a proxy. Check the login sample’s response code before the expression. -
The expression is wrong:
$.accessTokenwhere the API returnstoken. Paste it into the JSON Path Tester on the real response. -
The extractor looks at the wrong part: Field to check set to the body when the value is in a header (the new loan id in
Location), or Apply to set to sub-samples only. -
The match number:
0picks a random match, and-1writes the numbered variables (${loanId_1}) andloanId_matchNr, while${loanId}itself is the default, the first match or a stale value depending on the extractor.
Make the failure fail loudly: a Response Assertion on the variable that fails when it equals the default, and a
Result Status Action Handler that starts the next iteration. The defaults, the -1 rules and the assertion are on
Correlation & Extractors and
Assertions.
Plugin not loading
Symptom: the plan does not load. A .jmx that uses a plugin element names the plugin’s class. When that class is
not on JMeter’s classpath, the plan cannot be read at all. Reproduced on 5.6.3 with the Concurrency Thread Group of
jpgc-casutg not installed:
Error in NonGUIDriver java.lang.IllegalArgumentException: Problem loading XML from:'.../bookshelf-stress.jmx'.
Cause:
CannotResolveClassException: com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup
Detail:com.thoughtworks.xstream.converters.ConversionException:
---- Debugging information ----
...
path : /jmeterTestPlan/hashTree/hashTree/com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup
line number : 6
version : 5.6.3
The class name tells which plugin is missing; path and line number point at the element in the .jmx. In the GUI
the same error appears in a dialog when opening the file.
Symptom: the plugin is installed but its elements are not in the Add menu, or a sample fails with
NoClassDefFoundError.
| Cause | Fix |
|---|---|
JMeter was not restarted after the jar was added |
Class loaders are built at start-up: restart. |
The jar is in |
Only |
A library the plugin needs is missing from |
|
Two versions of a plugin or library on the classpath |
Remove the older jar; with the Plugins Manager, uninstall and reinstall. |
The plugin is installed on the client but not on a |
Install the same plugins at the same versions on every node. |
The plugin is not in the Plugins Manager dialog after a catalogue update |
Delete the |
The Plugins Manager refuses to apply changes |
The user running JMeter cannot write to its directories; install JMeter in a directory that user owns. |
In a Maven build, the plugin is not in the build’s JMeter |
List it in |
bin/PluginsManagerCMD.sh install-for-jmx bookshelf-stress.jmx reads the class names from the plan and installs the
plugins that provide them, which fixes the first symptom on a CI agent or in a Docker image. All of this, including the
discovery mechanism in 5.6 and the lib/lib/ext rules, is on
Plugins & Plugins Manager.
gRPC "method not found" and proto import errors
The gRPC Request plugin (2.0.2) fails in two places, and its response code says which:
-
response code
400: the request could not be prepared on the generator. The response message isGRPCSampler parsing exception: An unknown exception occurred before the GRPC request was initiated..., the response data holds the stack trace, and the sample takes almost no time; -
response code
500with a gRPC status such as12 UNIMPLEMENTEDin the response message: the call reached the server, which answered with an error.
The messages below are those of the 2.0.2 source (ServiceResolver, ProtoMethodName, ProtocInvoker); they appear
in the stack trace of the sample’s response data and in jmeter.log.
| Message | Cause | Fix |
|---|---|---|
|
The service was found in the compiled protos but the method name in Full Method is not in it (here a misspelling of |
Click Listing… next to Full Method and pick the method from the drop-down; names are case-sensitive. |
|
No |
Use the full form |
|
Full Method is not in the form |
Fix the format. |
|
|
Read the |
|
A pre-compiled descriptor set ( |
Rebuild it with |
|
The request JSON is not valid, often because an unquoted variable was empty or unresolved. |
Quote variables inside JSON strings and check the Request tab. |
Response message |
The server does not implement the method: the test’s |
Use the contract of the deployed version; regenerate descriptor sets whenever the |
Two gRPC failures look like server problems but are not: 14 UNAVAILABLE on every call after one earlier error (in
2.0.2 a failed call closes the thread’s channel and it is not rebuilt), and 4 DEADLINE_EXCEEDED under load because
the Deadline field kept its GUI default of 1000 ms. The plugin’s fields, its error mapping and these pitfalls are
on gRPC Load Testing.
Other messages worth recognising
| Message | Meaning |
|---|---|
|
Log4j start-up noise in 5.6.3, not a JMeter error. Its removal is planned for 6.0. |
|
A JVM warning caused by an option in the launcher; harmless. |
|
Normal: the UDP port for |
The summariser shows |
Expected: the exit code reflects whether the run could start, not whether samples passed. Gate CI on the results, as on Running Tests in CLI Mode. |
|
|
Load-generator cost of debugging
Every tool on this page is meant for one thread, not for the load run:
| Tool | Cost if left in a load run |
|---|---|
View Results Tree |
Keeps up to |
Debug Sampler |
One extra sample per iteration, counted in throughput and the dashboard, with a body of every variable. |
Debug PostProcessor |
A sub-sample per parent sample; with JMeter Properties ticked, hundreds of lines each time. |
Save Responses to a file |
Nothing while samples succeed; one file per failure, and one per sample when everything fails. |
DEBUG logging |
Several log lines per sample; wire logging writes every byte sent and received to disk. |
|
Prints every handshake and record; makes TLS throughput meaningless. |
The safe pattern is to keep debugging elements disabled in the committed plan and enable them only in the GUI, or to
gate them on a property (an If Controller on ${__P(debug,false)}) so that -Jdebug=true switches them on for a smoke
run.
Pitfalls
-
Trusting the exit code. A run where every sample failed still exits with 0; read the summariser and the JTL.
-
Trusting a quiet summary.
0 in 00:00:00withErr: 0means threads died before sampling, usually a missing file. -
Debugging under load. Errors seen only at 50 threads cannot be diagnosed with View Results Tree; reproduce the data or correlation part with one thread and look at capacity with the dashboard and server metrics.
-
Leaving DEBUG on. A
-L...=DEBUGcopied into a CI script turns the generator’s disk into the bottleneck. -
Sharing logs and debug output. Wire logs, Debug Sampler output and XML results with request data contain tokens and passwords; keep them out of tickets and repositories.
-
Fixing the symptom. Raising the heap for a View Results Tree left in the plan, or adding retries for
NoHttpResponseException, makes the run finish and the results wrong. -
Assuming the books' advice still applies.
log_level.*properties injmeter.properties, BeanShell debug prints and plain RMI without a keystore are from before 3.2 and 4.0.
From the books to 5.6 to 6.0
| Topic | Books (JMeter 2.11 to 3.0) | 5.6.3, and planned 6.0 |
|---|---|---|
Logging configuration |
|
|
Log viewer |
Log viewer in the GUI |
Options → Log Viewer, Options → Log Level, an error counter in the toolbar |
Heap |
Edit |
|
Remote exceptions |
RMI without SSL; connection refused usually meant firewall or host name (Pro ch. 8) |
RMI over SSL by default since 4.0, so a missing |
Debugging elements |
Debug Sampler, View Results Tree, BeanShell for printing values |
Same elements, plus the JSON Path, JMESPath, Boundary and XPath2 testers; JSR223 + Groovy with the |
Plugins |
Unzipped plugin sets (Cookbook appendix) |
Plugins Manager and |
Java and start-up warnings |
Java 7 or 8 |
Java 8+ for 5.6.3, 17+ recommended; 6.0 plans to require Java 17 and to remove the Log4j package-scanning warning |
References
-
Getting Started (Apache JMeter User Manual): logging and error messages,
-L,-j,-i, the heap, classpath configuration, SSL. -
Hints and Tips (Apache JMeter User Manual): enabling debug logging, the log viewer, searching, the Browser renderer.
-
Component Reference (Apache JMeter User Manual): Debug Sampler, Debug PostProcessor, View Results Tree, Save Responses to a file, CSV Data Set Config, HTTP Request, HTTP(S) Test Script Recorder, extractors.
-
Properties Reference (Apache JMeter User Manual):
view.results.tree.*,testplan_validation.*,jmeter.loggerpanel.*,httpclient4.*, SSL and RMI properties. -
Remote Testing (Apache JMeter User Manual): RMI keystore, ports, host names.
-
Changes History and apache/jmeter on GitHub:
bin/log4j2.xmlandbin/jmeter.propertiesatrel/v5.6.3, planned 6.0 changes inxdocs/changes.xmlon master. -
HttpClient Logging Practices (Apache HttpComponents): the
org.apache.http.wireandorg.apache.http.headerscategories. -
jmeter-grpc-request (bakthava): README and the 2.0.2 sources.
-
Sai Matam and Jagdeep Jain, Pro Apache JMeter, Apress, 2017: chapter 8 (troubleshooting: logs, heap, proxy and remote exceptions).