skip to content

In a Karate feature file, where must a `retry until` step sit relative to the `method` step, how many times does Karate call the endpoint by default, and what is waited between attempts?

level: middleimportance: should knowfreq 52%

answer

  1. It modifies a call, it does not wait
  2. Order relative to the firing step matters
  3. Both defaults are small numbers
  4. count is total attempts, not extra ones

basics

~20 s

retry until must come before the method step it applies to. The defaults are three attempts in total with a 3000 millisecond pause between them, and both are changed with configure retry set to a count and interval.

solid answer

~50 s

`retry until <js expression>` arms the *next* `method` step; it is not a step that waits by itself. Karate then calls the endpoint, rebinds `response` and `responseStatus`, and evaluates the expression — repeating until it yields boolean `true` or the attempt budget runs out, at which point the step fails. The defaults are **count 3 and interval 3000 ms**, and `count` is the total number of calls, not extra ones on top of the first: three calls with two pauses between them. Override with `* configure retry = { count: 10, interval: 5000 }`. The condition is plain JavaScript, so `match` vocabulary such as `contains` is not available there. And because the retry setting is cleared along with the rest of the request state after every call, a `retry until` written *after* `method` is not an error — it silently arms the following call instead.

code

gherkin · 10 lines
gherkin
Background:
  * url 'https://api.example.com'
  * configure retry = { count: 10, interval: 5000 }

Scenario: poll an async job until it reports done
  Given path 'jobs', jobId
  And retry until responseStatus == 200 && response.state == 'DONE'
  When method get
  Then status 200
  And match response.result.rows == '#number'

go deeper

for a junior

Remember the ordering and the two defaults: the condition line goes above the method line, and out of the box it is three attempts three seconds apart.

for a middle

Explain why the ordering rule exists — the condition is state on the request builder that the firing step reads — and that count is the total number of calls.

for a senior

When a polled test fails fast or always burns its whole budget, check the step order and whether the expression throws before you touch the service under test.

for a principal

Whether an endpoint should be polled at all, and where retry belongs in a suite, is a separate design call; this keyword only gives you the mechanism to express one you have already decided on.

## `retry until` is a modifier on the next call, not a wait step It is easy to read `retry until` as "pause here until the condition holds". It is not. The step writes a condition string onto the same request builder that `url`, `path` and `header` write into, and the `method` step that follows checks for it: if a condition is present, it runs the retry loop instead of a single invocation. ```gherkin Given url baseUrl And path 'jobs', jobId And retry until responseStatus == 200 && response.state == 'DONE' When method get Then status 200 ``` The condition therefore has to be written **above** the `method` step. That is the whole placement rule, and it follows from the mechanism rather than being an arbitrary convention. ## The loop, precisely For each attempt Karate: 1. Sleeps for the configured interval — **except before the first attempt**, which fires immediately. 2. Invokes the request. 3. Rebinds `response`, `responseStatus`, `responseHeaders` and `responseTime` from that attempt. 4. Evaluates the condition expression against those fresh variables. 5. Returns if the expression yielded `true`; otherwise restores the request state and goes round again. When the attempt count is reached without the condition holding, the step **fails** — it does not fall through with the last response for you to assert on. ## The defaults, and what `count` counts | Setting | Default | Meaning | |---|---|---| | `count` | 3 | total attempts, first one included | | `interval` | 3000 ms | pause *between* attempts | The most common misreading is that `count: 3` means one call plus three retries. It does not: the default budget is **three requests and two pauses**, so roughly six seconds of wall clock in the worst case, not twelve. Change both together: ```gherkin * configure retry = { count: 10, interval: 5000 } ``` That `configure` can go in a `Background`, in a single scenario, or in the config script that runs before the suite; like any `configure`, a later one in the same scenario overrides an earlier one. ## The condition is JavaScript, and it must be a boolean Two constraints catch people out: - **`match` vocabulary is not available.** `retry until response contains { state: 'DONE' }` does not work, because the text after `retry until` is evaluated as an expression, not parsed as a `match`. Write the JavaScript equivalent, or call a helper function. - **A truthy value is not enough.** The result has to be an actual boolean `true`. `retry until response.items.length` never succeeds even when the array is populated, because a number is not `true`. Write `retry until response.items.length > 0`. A third behaviour is worth knowing because it hides mistakes: if evaluating the condition **throws** — a typo, or a field that does not exist on an error body — the failure is logged as a warning and treated as "condition not satisfied". The loop keeps going and eventually reports that the retries were exhausted. A `retry until` that always burns its whole budget is very often a broken expression rather than a slow service. ## The silent placement failure The retry condition is stored on the request builder, and that builder is reset after every call — the condition is cleared along with the path, headers, body and params. So this reads plausibly and does nothing useful: ```gherkin # WRONG - the condition arms the NEXT call, not the one above it When method get And retry until response.state == 'DONE' Then status 200 ``` The `method get` above it ran exactly once. The condition then sits on a freshly reset builder, waiting for whatever `method` step comes next in the scenario — and if none does, it is simply discarded at the end. Nothing warns you. The symptom is a test that fails on the first poll of a slow endpoint while its author is convinced retrying is configured. ## Scope and hygiene Because the condition is cleared with everything else, `retry until` applies to exactly one call. Two polled calls in a scenario need two `retry until` steps. That is a feature: it is very hard for a retry to leak onto an unrelated request further down the scenario. The `configure retry` values, by contrast, are configuration and persist for the scope you set them in, so a feature that polls several endpoints usually configures the budget once and writes one `retry until` per polled call.

  • What happens if the expression after `retry until` throws while being evaluated?
    The error is logged as a warning and the attempt counts as "condition not satisfied", so the loop continues to the next attempt and eventually fails with the retries exhausted. A `retry until` that always uses its full budget is frequently a broken expression — a typo, or a field that is absent on the error body — rather than a slow service.
  • Why does `retry until response.items.length` never succeed?
    Because the result must be an actual boolean `true`, not merely a truthy value. A number, a string or an object all count as not-satisfied. Write a comparison that yields a boolean, such as `retry until response.items.length > 0`.
  • Does one `retry until` step cover every call that follows it in the scenario?
    No. The condition is stored on the request builder and cleared with the path, headers and body once the call fires, so it applies to exactly one request. Two polled calls need two `retry until` steps. Only the `configure retry` count and interval persist for the scope where they were set.

saying these in an interview costs you the question

  • Puts the retry step after the method step
  • Reads count 3 as one call plus three retries
  • Writes match or contains syntax in the condition
  • Expects a non-boolean truthy result to satisfy it
  • Thinks one retry step covers every later call
  • Assumes an exhausted retry leaves the last response to assert on