Skip to content

Debugging tools

Failure scenarios

Since 0.4.0 Requires Cloak Proxy 0.4.0 or later.

Break the network the ways your app must survive with Cloak Proxy's failure scenarios. Fail the first N requests, a percentage, return a status, time out, reset the connection, drop or truncate a body, trickle a response or serve stale data, and see what each fault did to each request.

Since 0.4.0

Apps break in production where the network misbehaves: a request that fails twice and then works, a payment call that never answers, a download that stops halfway, a cache that never refreshes. Network Conditions slows everything down. Failure scenarios break specific requests in specific ways, on a schedule you choose, so you can watch the app’s retry logic, timeouts and error screens do their job.

A scenario is a named group of faults that you switch on and off as a whole, such as “Flaky checkout”. Each fault says which requests it breaks (a URL pattern, method, apps and conditions, such as one GraphQL operation), when (every time, the first N times, after N successes, only the Nth time, or a percentage), and how. A log records what each fault did to each request.

A scenario with four faults, how many requests each matched and broke, and the selected fault's editor.

The Failure Scenarios window. Flaky checkout is on, with four faults: Checkout fails twice (POST api.shopapp.example/graphql, operation is CreateOrder, the first 2 times then succeed, 3 matched and 2 broken), Time out after 20 s on the payments API only the 2nd time, Cut the body to 50% on /v1/config 30% of the time, and Trickle at 1 KB/s turned off. Below, the selected fault's editor.
  1. Choose Tools › Failure Scenarios… (⌥ ⌘ F). Or right-click a request in the traffic list and choose Simulate a Failure…: the new scenario’s fault matches that request (and its GraphQL operation) and fails it twice before letting it through.

  2. Start from a template, or click + and choose New Scenario. Templates: Flaky API, Random failures, Server down, Timeouts, Connection resets, Dropped downloads, Slow endpoint, Truncated JSON, Stale data and Session expires.

  3. Select a fault and set the URL pattern it breaks under Requests. Add conditions to narrow it, such as GraphQL operation is CreateOrder or Header X-App-Version matches ^4.. The line under the conditions shows how many captured requests the fault would match.

  4. Choose What goes wrong and When.

  5. Switch the scenario On, then use the app. The fault’s matched and broken counts go up as requests arrive, and the Log tab lists each one.

On Linux, the window opens with Ctrl Alt F, or by right-clicking a request and choosing Simulate a Failure….

Scenarios start off, and stay off until you switch them on. Several can be on at once. Switching one on starts its counts from 1 and clears its log, so “the first 2 times” means the first two requests after you switched it on. Reset Counts does the same without switching it off and on.

Fault What the app sees Settings
Return a status and body The response you write. The server isn’t contacted. Status, headers and body (default 503 with a JSON error).
Time out No answer; after the wait the connection closes. Set it longer than the app’s own timeout to see the app give up first. Seconds (default 30).
Reset the connection The connection is reset (a TCP RST) before any answer, as when a network or load balancer drops it.
Drop the connection mid-body The real response’s headers and the first part of its body, then the connection closes. The body is shorter than its Content-Length. Percentage of the body sent (default 50).
Trickle the response slowly The real response, a little at a time. Bytes per second (default 2,048).
Delay the request The real response, after a wait before the request goes to the server. Milliseconds (default 3,000).
Truncate the body The real response with its body cut short and Content-Length to match: valid HTTP, broken JSON or images. Percentage of the body kept (default 50).
Serve stale data The first response seen after the scenario went on, again and again, as if a cache never refreshed. Requests are told apart by method, URL and GraphQL operation.

Combine faults in one scenario for a richer case: Delay and Trickle on the same endpoint make a slow endpoint; a status fault for one GraphQL mutation and a timeout for the payment provider make “Flaky checkout”.

Trigger Breaks the matching request
Every time Always.
The first N times, then succeed Requests 1 to N; later ones go through. Tests retries that eventually work.
After N successes Every request after the first N. Tests a session or quota running out.
Only the Nth time Just request N, for example only the second CreateOrder.
A percentage of the time That share of requests, at random.

Counting is per fault: each counts the requests it matches. When several faults match one request, the first that fires (top to bottom, across scenarios in the list’s order) breaks it, and the others log Let through (an earlier fault applied).

The Log tab lists every request a fault matched, newest first:

What each fault did to each request.

The Log tab: eight rows with time, request number, which match it was (1st, 2nd, 3rd), the request, the fault, and the result, such as Answered 503 Service Unavailable without contacting the server, Held for 20 s then closed without an answer, Cut the body to 50%, or Let through (the first 2 times, then succeed).
Column Meaning
# The request’s number in the traffic list. Double-click a row, or right-click it and choose Show Request, to select it there.
Match Which matching request this was since the scenario went on: 1st, 2nd…
Fault The fault’s name, or what it does.
Result What was done, in red, or Let through and why, in grey.

In the traffic list, broken requests are tagged Failure Scenario. A reset shows as failed with Connection reset by failure scenario “…”; a timeout as failed with Timed out: no answer for N s; a dropped body as Connection dropped after N of M bytes, with the part that was sent as its response.

The toolbar’s tools popover (the N tools on button in the bottom bar) has a switch for each scenario, and the button’s tooltip lists Failure Scenarios while one is on.

The MCP server’s failure_scenarios tool lists scenarios with each fault’s counts, lists the templates, creates a scenario from a template for a URL (and conditions) or from a full definition, updates, switches on and off, resets, deletes, and returns the log. For example, an assistant can create “Flaky API” for api.example.com/v1/orders*, switch it on, have you try the app, and read back what happened.

With cloakctl, for a test run:

Terminal window
cloakctl scenarios create --template "Flaky API" --url 'api.example.com/v1/orders*' --on
npm run test:e2e
cloakctl scenarios log "Flaky API"
cloakctl scenarios off "Flaky API"

--operation CreateOrder limits a template’s faults to one GraphQL operation. cloakctl scenarios alone lists every scenario with its faults’ counts. Commands exit with 1 when the app isn’t running or a scenario doesn’t exist.

Scenarios are saved with your rules: Tools › Export Rules… includes them, Import Rules… adds them, and cloakctl run --tools applies them in headless runs.

Test suites that use Toxiproxy can drive scenarios through the Toxiproxy-compatible API.

Faults are checked right after Block List and Rewrite’s request actions, against the request as the app sent it. Status, timeout, reset and a kept stale response answer for the server, so nothing after them runs. Delay waits before scripts, breakpoints and the server. Truncate, drop, trickle and stale (the first time) let the request reach the server and change how its response is delivered; those responses aren’t streamed to the app as they arrive.

  • Scenarios break requests for real: switch them off when you’re done. The bottom bar’s tools button shows when one is on.
  • Counts, kept stale responses and the log live in the running proxy. They start again when the proxy restarts (for example after changing its port). The log keeps the last 2,000 events.
  • After a drop, trickle, reset or timeout, the app’s connection is closed; it opens a new one for its next request.
  • Requests over HTTP/2 can’t be reset with a TCP RST; their stream is closed instead.
  • Replays from Repeat and Compose go through scenarios too. With no app connection to break, they record what the app would have seen.