MuleSoft Until Successful: Retry Without Hiding Failure
MuleSoft's Until Successful scope retries the processors inside it until they succeed or the configured retry limit is exhausted.
That makes it useful for short-lived failures such as temporary network issues or brief downstream unavailability.
It does not make every failed operation safe to retry.
Basic Behavior
Conceptually:
execute processors
│
├── success → continue flow
│
└── failure
│
▼
wait
│
▼
retry
MuleSoft documents Until Successful as synchronous. If the configured retries are exhausted, the scope raises MULE:RETRY_EXHAUSTED.
A simple XML example is:
<until-successful
maxRetries="5"
millisBetweenRetries="3000">
<http:request
config-ref="Target_HTTP"
method="POST"
path="/resource" />
</until-successful>
This attempts the processors in the scope again after failures, up to the configured limit.
Retry the Smallest Safe Unit
Suppose a flow does this:
transform payload
create record in System A
create record in System B
send notification
Wrapping the entire sequence in Until Successful means a failure in the notification step can cause the earlier create operations to run again.
That may duplicate data.
A safer design is often to retry only the operation that is both transient and safe to repeat:
transform payload
│
create System A record
│
Until Successful
└── call System B
│
send notification
The retry boundary should match the idempotency boundary.
Variable Behavior Matters
MuleSoft documents an important detail: each retry starts with the variable values that existed before entering the Until Successful scope.
Changes made during a failed attempt are not carried into the next attempt.
That behavior can be helpful because retries begin from a consistent state, but it also means you should not depend on an attempt counter or intermediate variable mutation inside the scope unless you design for that behavior explicitly.
Good Candidates for Until Successful
Typical examples include:
- a temporary HTTP 5xx response;
- a short network interruption;
- a downstream service restarting;
- a transient connection failure;
- a brief throttling condition when retry timing is appropriate.
The common characteristic is that another attempt soon afterward has a reasonable chance of succeeding.
Bad Candidates for Immediate Retry
Some failures are unlikely to improve with repetition:
invalid payload
missing mandatory business data
authorization permanently denied
unsupported field
schema mismatch
business-rule rejection
Retrying those conditions five times usually produces the same failure five times and wastes capacity.
These failures are better routed toward validation, remediation, or a durable recovery workflow.
Ambiguous Outcomes Need Special Care
Consider:
MuleSoft ── POST ──▶ Target
│
├── target creates record
└── response is lost
MuleSoft sees timeout
From MuleSoft's perspective the request failed. From the target system's perspective it may have succeeded.
If Until Successful immediately repeats the POST, the target might create the record again.
Before retrying create-style operations, ask whether the target supports:
- idempotency keys;
- upsert by stable business key;
- External IDs;
- lookup/reconciliation after ambiguous failures;
- another uniqueness mechanism.
Retry is safest when the business effect itself is idempotent.
Avoid Retry Storms
If many Mule workers encounter the same downstream outage at once, each can begin retrying.
That can turn a small failure into additional load on an already unhealthy dependency.
Think about:
number of concurrent requests
× retries per request
× retry frequency
For example, 1,000 requests each retrying five times can create thousands of additional calls during an outage.
Use bounded retries and a sensible interval. For longer outages, stop synchronous retrying and hand the work to a durable recovery path.
Until Successful vs Durable Replay
Until Successful is a short-term synchronous retry mechanism.
A durable recovery workflow solves a different problem.
brief transient issue
→ Until Successful
long outage / data correction / operator action
→ durable failure record + later replay
Trying to keep a request alive for an extended outage can tie up resources and create poor operational behavior.
This is where a DLQ, Error Hospital, persistent retry queue, or another recovery pattern becomes useful.
Decide What Happens After Retry Exhaustion
Do not stop the design at:
maxRetries = 5
Ask what happens on attempt six.
Possible outcomes include:
- propagate the error to the caller;
- route the failed business record to a recovery store;
- publish a failure event;
- create an operational alert;
- persist a source identifier for later re-fetch;
- mark a synchronization record as failed.
The correct behavior depends on whether the work can be safely abandoned or must eventually complete.
Retry Only Expected Errors
A useful pattern is to classify errors before applying retry behavior.
For example:
HTTP 503 → retry
connection timeout → retry if operation is idempotent
HTTP 400 → do not retry
business validation failure → do not retry
Not every exception should enter the same retry loop.
Practical Rule
Use Until Successful for bounded, synchronous retry of transient failures when repeating the protected operation is safe.
If the operation is not idempotent, the outage may last a long time, or human/data remediation is required, use a recovery architecture instead of increasing the retry count.