Skip to content

Inspect error response bodies

A status code tells you something failed. The captured request and response tell you what. MaxoPerf groups failed requests by error type on the run’s Errors tab and keeps redacted samples of the request and response for the engines that expose them, so you debug from real data instead of guessing.

Read your test results.
  • A 4xx burst that looks like an auth issue but may be a validation error.
  • A 5xx whose body names a specific upstream service.
  • A test scenario that gets unexpected redirects or HTML error pages.
  1. Open the run and switch to the Errors tab.

    Run detail, Errors tab.
  2. Pick a view. By error type groups rows by category and signature, so you see which kinds of failure dominate. By transaction groups the same errors by request label, so you see which requests fail.

  3. Select a row. Move between rows with the arrow keys and open one with Enter, or click it. The row shows the category, the signature, how many requests hit it, its share of requests, when it was first and last seen, and a trend sparkline. In By error type, the detail pane also lists the request labels that hit the error.

  4. Open a sample. Use the First, Last, or Recent tab to load a captured sample. Each sample shows the request line, headers and body, and the response status, headers and body.

  5. Read the body. For JSON and XML bodies, switch between Pretty and Raw, toggle line wrap, or copy the text. A binary body downloads as a file instead of rendering inline.

  6. Check the logs. The Logs tab shows what the runner did at the same timestamp. With the logs and the response body you usually know what to fix.

MaxoPerf normalizes each error message into a signature so that the same failure reported with different ids collapses into one row. For example, 404 Product 'a-1' not found and 404 Product 'b-2' not found both produce the signature 404 Product '<str>' not found.

The masking rules:

  • UUIDs become <uuid>.
  • Hex ids of 8 or more characters become <hex>.
  • Email addresses become <email>.
  • Numbers of 2 or more digits become <n>.
  • Quoted text becomes '<str>'.
  • Absolute URLs keep their scheme and host; the path becomes /<path> (https://demo.maxoperf.com/api/products/blue-shirt becomes https://demo.maxoperf.com/<path>).
  • A bare path after for url: (Locust’s error format) becomes for url: /<path>, so Locust 404s for different slugs share one row.
  • Query strings become ?<query>.
  • 0x… addresses become <addr>.
  • Values that look like secrets never survive into a signature: a password=…-style value becomes <redacted>, a Bearer … token becomes Bearer <redacted>, a Basic … value becomes Basic <redacted>, and JWTs become <jwt>. Credentials in a URL (https://user:pass@host) become https://<redacted>@host.

Every row also carries a category: Server errors (5xx), Client errors (4xx), Timeouts & network, Assertion failures, or Other errors.

Each row also shows the share of requests that hit the error: of all requests in By error type, of that request label’s requests in By transaction. It shows a dash while any scope filter (time range, location, label, runner, scenario, or the Transactions filter) is active, because the percentage would no longer describe the whole run.

ExecutorRequest and response bodies
JMeter, k6, Gatling, Locust, Apiritif, PlaywrightCaptured
SeleniumChrome only, while browser recording is on for the run and the test’s first execution is Selenium. Samples appear when each browser session ends. Edge and Firefox record metadata only.
ab, Siege, pbench, Tsung, Vegeta, Molotov, Grinder, JUnit, TestNG, Mocha, WebdriverIO, RobotNot captured. The Errors tab shows messages and counts.

A few limits to know:

  • In a test with more than one execution, k6 samples come from the first execution only.
  • Selenium samples need the test’s first execution to be Selenium; then every Selenium execution in the run is covered.
  • Locust users built on FastHttpUser are not captured; HttpUser is.
  • Gatling samples are skipped for the whole run when the test sets its own JAVA_OPTS or logback.configurationFile, or when its files include a logback*.xml. Errors still show in the list, without samples.
  • k6 samples come from virtual users 1 and 2 only. An error that only other virtual users or scenarios hit still shows in the list, but has no sample.
  • Requests sent with k6 http.batch or http.asyncRequest are not captured.
  • Locust custom failures (response.failure(...), CatchResponseError) are recorded by Taurus with code 500, so they appear under Server errors (5xx), unless the failure message names a timeout, a connection error, or an assertion.
  • Playwright test failures (Test failed: …) are recorded by Taurus with code 500 or the page’s last navigation status; they appear under Assertion failures, or under Timeouts & network for timeouts and connection errors, never under Server errors (5xx).

For Selenium, browser recording must be on for the run. See Record browser tests: video, HAR, and console steps.

For each combination of runner, scenario, request label, and signature, MaxoPerf keeps the first sample, the last sample, and up to 5 of the most recent samples. A body over 64 KiB is cut and marked Truncated. A run keeps samples for up to 500 distinct combinations of scenario, request label, and error per runner; beyond that, the error is still counted but has no sample. Samples are kept for as long as the run’s other results.

Samples appear while the run is in progress, except for Selenium, where they appear when each browser session ends.

The sample pane tells you which sample it is showing. It prefers a sample that matches this exact error; if there is none, it falls back to a sample with the same status code from the same request label; failing that, it falls back to any sample for that label, and says so. When the Transactions filter is set to anything other than All transactions, the pane shows no samples and says “Samples aren’t linked in filtered views”. Set it back to All transactions to see them.

MaxoPerf redacts captured samples before they leave the runner: the request and response bodies, the request URL, headers, and the response message.

  • Headers: a fixed list (Authorization, Proxy-Authorization, Cookie, Set-Cookie, X-Api-Key, X-Auth-Token, X-Amz-Security-Token), plus any header whose name contains a secret word, such as Api-Key, X-Access-Token, Private-Token, X-CSRF-Token, or X-Session-Id. A redacted header shows a Redacted badge.
  • Bodies, URLs, and messages: JSON, form, and query fields whose name contains password, passwd, secret, token, api_key/api-key/apikey, authorization, access_key/access-key, client_secret/client-secret, session, or cookie; XML and SOAP elements with such a name (for example <wsse:Password> or <apiKey>); multipart form fields with such a name; credentials in a URL (https://user:pass@host becomes https://[REDACTED]@host); and any Bearer … or Basic … value, or a JWT, becomes [REDACTED].
  • Redaction is pattern-based, matching on field and header names, not on a full understanding of the payload.
  • A binary body (one that is not valid UTF-8 text) is not redacted. It is kept as captured and downloads as a file.

Transaction names are shown exactly as your test names them. They are not redacted, so keep secrets out of your labels.

The row’s message and sample messages shown in the Errors list (and in get_run_errors) are not redacted. They come through as the engine reported them. Only captured samples go through redaction.

Engine files you download from the Artifacts tab, such as JMeter’s error.jtl, and browser recording HAR files, contain raw requests and responses and are not redacted. Redacting them is planned.

Error body capture is on by default. Turn it off (or back on) from a single test’s Configuration tab, under Test details: the Capture error bodies switch saves immediately, with no separate Save step, and applies to the test’s next run. It is only available for single tests. A VarioTest master has no switch of its own; each member uses the setting of its source test.

A re-run of an earlier run reuses that run’s original bundle, so it keeps that run’s capture setting. Runs recorded before this feature shipped stay without bodies even when re-run.

See Test detail: Configuration.

Runs from before error grouping shipped still show their errors, grouped by status code under a Recorded before error grouping banner instead of by category and signature.

If the captured body is empty or does not help (for example, an HTML error page that does not name the cause), try these next:

An MCP-connected assistant reads the same data. get_run_errors returns the grouped rows, each with a signature, category, label, count, responseCode, and a sample message. Follow up with get_run_error_bodies, filtered by signature from that row. If that returns nothing, because the sample’s own error text differs from the row’s status line, retry with the row’s label and response_code, then with label and the row’s category. Never retry with label alone, because that also returns the bodies of other errors on the same transaction. Body text comes back only when you pass a specific row_id: the default concise format returns a short snippet, and response_format: "detailed" returns the full redacted body.

To turn capture off when creating a single test, pass capture_error_bodies: false to create_test (the server default is true). VarioTests take the setting from each member’s source test. See MCP tools reference.