Developer track · 4 min read

Continue or propagate? How Mule 4 error handling decides success vs failure

By the MulePrep team · Updated June 2026

On Error Continue

  • Handles the error, owning scope COMPLETES successfully
  • Caller receives the result of the error handler
  • Use when the failure is recoverable / optional

On Error Propagate

  • Runs the handler, then RE-THROWS to the level above
  • Owning scope fails; the error bubbles up
  • Use when the caller must know it failed

Mule 4 gives you two error-handler scopes, On Error Continue and On Error Propagate, and almost every confusing error-handling bug comes from not internalising the single difference between them. That difference is not "one logs and one doesn't" or "one is for retries". It is whether the event is marked handled. Get that one idea straight and the rest, flow return values, transactions, retries, and router behaviour, all follow.

This guide teaches the model from first principles, with neutral worked examples, so you can reason about any error-handling question instead of memorising cases.

The "event handled" model: the one bit that changes everything

When a component raises an error in Mule 4, the runtime looks for an error handler in the current scope and runs the first handler whose error type matches. Both handler scopes run their inner components fully. The difference is what they do after:

  • On Error Continue marks the event as handled. The owning scope (the flow, or a Try scope) is treated as successful, and its result becomes whatever the handler produced.
  • On Error Propagate leaves the event in an error state. After the handler runs, the error is re-thrown to the next level up, so the owning scope is treated as failed.

That single bit, handled or not, is the whole game.

On Error Continue

  • Handles the error, owning scope COMPLETES successfully
  • Caller receives the result of the error handler
  • Use when the failure is recoverable / optional

On Error Propagate

  • Runs the handler, then RE-THROWS to the level above
  • Owning scope fails; the error bubbles up
  • Use when the caller must know it failed

On Error Continue: return the handler result as a successful outcome

Reach for On Error Continue when the failure is recoverable or optional, and the caller should still get a sensible result. A classic case: an enrichment call to a non-critical system fails, and you would rather return the core payload with a default value than fail the whole request.

Inside the handler you typically set a fallback payload. Because the scope completes successfully, the flow returns that payload and the caller never sees an error. Use it when the answer to "should the caller still succeed?" is yes.

On Error Propagate: run the handler, then re-throw to the caller

On Error Propagate is for failures the caller must know about. The handler still runs, so you can log the error, send a notification, or perform compensation. But once it finishes, the error is re-thrown and the owning scope fails.

This is the right choice when continuing would be incorrect: a required validation failed, a payment could not be taken, a mandatory downstream system is unavailable. You want the failure to surface so the caller (or an outer Try) can react.

How it interacts with flow-ref, nested Try scopes, and the default error handler

Error handling composes through references and nesting:

  • A Try scope has its own error handler. If it uses On Error Continue, the error is absorbed at the Try and the surrounding flow continues normally. If it propagates, the error escapes the Try into the flow that contains it.
  • With flow-ref, an error in the referenced flow propagates back to the calling flow unless the referenced flow handles it with Continue. Treat a flow-ref like a method call: an unhandled error comes back to the caller.
  • If no handler matches, the default error handler applies, and it propagates. So an unmatched error behaves as if you had written On Error Propagate.

Handlers are matched top to bottom, first match wins, with the broadest matcher (ANY) conventionally last. Ordering your error types from most specific to most general is what makes the matching predictable.

Transactions: why handled vs re-thrown decides commit vs rollback

This is where the model pays off. If a scope owns a transaction:

  • On Error Continue marks the scope successful, so the transaction commits.
  • On Error Propagate fails the scope, so the transaction rolls back.

So "do I want this work committed or rolled back on error?" is the same question as "continue or propagate?". If a half-finished unit of work must not be persisted, you must propagate; quietly continuing would commit a partial transaction.

QuestionOn Error ContinueOn Error Propagate
Owning scope outcomeSuccessfulFailed
Flow return valueHandler resultRe-thrown error
Transaction it ownsCommitsRolls back
Does the caller see an error?NoYes
Use whenFailure is recoverable / optionalCaller must know and react

Inside routers: scatter-gather and until-successful wrap child errors

Routers add a twist, because they do not surface a child error directly:

  • Scatter-gather runs branches in parallel and aggregates results. If any branch fails, it raises a single composite routing error describing all the failures, rather than handing you one branch's error. You handle that aggregate in the surrounding flow; a single branch's On Error Continue changes only that branch, not the composite outcome.
  • Until Successful retries its scope on failure up to a configured number of attempts, with a delay between them. It is built for transient faults, so reserve it for failures that are likely to clear on a retry, not for permanent ones.

The lesson: when an error originates inside a router, reason about the router's error behaviour first, then decide where your continue or propagate handler belongs.

A decision table: does the caller need to see the error and roll back?

When you are unsure, ask two questions in order:

  1. Should the caller still succeed despite this failure? If yes, On Error Continue. If no, On Error Propagate.
  2. Must any in-flight transactional work be undone? If yes, you must propagate; continuing would commit partial work.

Everything else, logging, notifications, fallback payloads, is something both handlers can do. The scope you pick is really a single decision: mark this event handled, or let it fail. Once you see error handling as that one bit, the exam questions and the 3 a.m. production incidents both get a lot easier to reason about.

Frequently asked questions

Does On Error Continue swallow the error completely?
It stops the error from propagating, but it does not pretend nothing happened. The handler still runs, so you can log, transform, or set a fallback payload. The owning scope then completes successfully and returns whatever the handler produced, so the caller sees a normal result rather than a failure.
Will On Error Propagate still run my logging or cleanup logic?
Yes. Both handlers execute their components fully before deciding what to do next. On Error Propagate runs everything inside it, then re-throws the error to the next level up. So logging, compensation, or notification steps inside the handler always happen, regardless of which scope you choose.
Which one rolls back a transaction in Mule 4?
On Error Propagate rolls back. Because the error is re-thrown, the owning scope ends in a failed state and a transaction it owns is rolled back. On Error Continue marks the event handled, so the scope completes successfully and the transaction commits. The choice of handler is effectively the choice of commit vs rollback.
What happens if no error handler matches the error type?
Mule applies the default error handler, which propagates. Error handlers are evaluated top to bottom and the first matching error type wins, so order matters. If nothing matches, the error bubbles up as if propagated, and an unhandled error fails the flow and returns an error to the caller.
How does error handling behave inside a scatter-gather scope?
Scatter-gather runs its routes in parallel and, if any route fails, raises a single composite routing error that aggregates the failures rather than surfacing one child error directly. You handle that composite error in the surrounding flow; you cannot rely on one route's On Error Continue to quietly fix the aggregate result.

Independent study resource - not affiliated with, endorsed by, or connected to MuleSoft or Salesforce; their trademarks belong to their owners. All practice questions are original.