---
title: Mule apps and flows
description: What a Mule app is made of, how one Mule event moves through a flow, what On Error Propagate and On Error Continue send back to the client, and how Flow Reference, Choice, Scatter-Gather and For Each change the path. Five live diagrams and a three-question check.
date: 2026-10-21
level: Beginner
tags: MuleSoft, Mule 4, Flows, Error handling
---

A Mule app is a set of flows, and a flow is the path one Mule event takes from a source to the end. Get those two ideas right and the rest of Mule 4 gets a lot easier: error handlers, routers, even why your payload vanished after an HTTP call.

This lesson follows one small orders app through five live diagrams. Each one plays by itself while it's on screen. The buttons above it change the story: take the stock API down, swap the error handler, pick a router. Pause it any time and step through with the arrows.

## One app, many flows

A Mule app ships as one JAR. The flows live in XML files under `src/main/mule`, next to a `pom.xml` and a `mule-artifact.json` that lists those files. You deploy that JAR to CloudHub 2.0, to Runtime Fabric or to a Mule runtime on your own servers.

Flows don't own their connections. A global element, like an HTTP Listener config or a Database config, is declared once, and every flow in the app points at it. Anything that changes between environments goes in a property placeholder such as `${http.port}`, not in the XML.

<figure class="lab" data-lab="app" data-desc="The orders app: two flows, post-order started by an HTTP request and nightly-report started by a scheduler, both using shared global elements for the HTTP listener, the stock API and the database.">
</figure>

Each flow starts from its own source. post-order waits for a request, nightly-report waits for 02:00. They share configuration, never an event.

## The Mule event is what moves

Everything a flow does, it does to a Mule event. The event has two parts: the message, which is a payload plus attributes, and the variables.

- `payload` is the data. For an HTTP request, it's the body.
- `attributes` say where the data came from: method, path, headers, query parameters.
- `vars` are yours. Set Variable writes them, and they stay with the event for the rest of the flow.

The catch: almost every processor returns a new message. Call another API with HTTP Request and its answer becomes the payload, its status code lands in the attributes, and the order the client sent is gone. Unless you saved what you need in a variable first, or told the call to put its result somewhere else with `target`.

<figure class="lab" data-lab="event" data-desc="One order going through post-order: the HTTP Listener turns the request into a Mule event, Set Variable saves the SKU, and the HTTP Request either replaces the payload with the stock answer or, with target=&quot;stock&quot;, stores it in vars.stock and leaves the message unchanged.">
</figure>

Switch to `target="stock"` and watch the payload row. It doesn't flash at the HTTP Request step this time: the answer lands in `vars.stock` and the order is still the payload when the listener replies. The Logger, in both runs, changes nothing.

## Errors take another path

When a processor fails, Mule raises an error with a type like `HTTP:CONNECTIVITY`, skips the rest of the flow and goes to the flow's error handler. The handler runs the first On-Error whose type matches. There are two kinds, and they answer the client very differently.

- On Error Propagate runs its processors, then throws the error again. The HTTP Listener answers 500 with the error's description as the body. The payload you set in the handler isn't sent unless you configure the listener's error response.
- On Error Continue runs its processors, then ends the flow as a success. The listener answers 200 with whatever the payload is.

And if the flow has no handler at all, Mule's default one logs the error and propagates it.

<figure class="lab" data-lab="errors" data-desc="post-order calls the stock API. When the API is up, the flow ends with 200. When it is down, the HTTP Request raises HTTP:CONNECTIVITY: with On Error Propagate the client gets a 500 with the error description, with On Error Continue it gets a 200 with the handler's fallback payload.">
</figure>

Run "down" with On Error Continue and look at the result: the stock API is down and the client gets a 200. Sometimes that's exactly what you want, a fallback answer. If the client needs to know something failed, set the status code yourself.

## Flow Reference: sub-flow or flow

Flow Reference calls another flow of the same app with the same event. Variables travel with it, so the called flow can read `vars.sku`, and the payload it leaves behind is the caller's payload when it returns. What you call comes in two shapes.

- A sub-flow has no source and no error handler. Mule puts its processors in place of the Flow Reference when it builds the app, so it really is part of the caller.
- A flow without a source can have its own error handler. An error inside it goes to that handler first, and only reaches the caller if the handler propagates it.

<figure class="lab" data-lab="flowref" data-desc="post-order calls check-stock with a Flow Reference. If check-stock is a sub-flow and the stock call fails, post-order's own error handler runs and the client gets a 500. If check-stock is a flow with its own On Error Continue handler, that handler runs instead, the Flow Reference returns normally and post-order answers 200.">
</figure>

So when check-stock has its own On Error Continue, post-order never sees the failure. Its Flow Reference just returns and the Logger runs. Great for a fallback, and easy to miss when you're reading logs and wondering why the caller's handler stayed quiet.

## Routers and scopes change the path

A flow doesn't have to be a straight line.

- Choice tests its routes in order and runs the first one whose condition is true. If none is, the `otherwise` route runs; it's optional.
- Scatter-Gather sends the event to every route at once, each on its own thread, and waits for all of them. The result is one payload with a key per route: `0`, `1` and so on.
- For Each runs its processors once per item of a collection. It doesn't change the payload: after the loop you get the original back. Variables you set inside the loop stay set.

<figure class="lab" data-lab="routers" data-desc="Three flows. A Choice router sends an order to the Nantes route for FR, the New Jersey route for US, and to a Raise Error in the otherwise route for any other country. A Scatter-Gather calls the stock API and the price API at the same time and returns one object keyed 0 and 1. A For Each reserves stock once per order item and leaves the payload as the original order.">
</figure>

Scatter-Gather is the one to reach for when two calls don't depend on each other: the quote waits for the slower call, not for both in a row. If a route fails, Mule still waits for the others, then raises `MULE:COMPOSITE_ROUTING` with what failed and what worked inside.

## Check yourself

Three questions on what you just ran. Pick an answer to see why.

<ol class="quiz" aria-label="Check yourself">
<li data-answer="1"><p class="quiz-q">The HTTP Request in post-order has <code>target="stock"</code>. After it runs, what is the payload?</p><ul class="quiz-options"><li>The stock API's answer</li><li>The order the client posted</li><li>Nothing: the payload is cleared</li></ul><details class="quiz-why" id="quiz-why-1"><summary>Answer</summary><p>The order the client posted. With <code>target</code>, the call's result goes into <code>vars.stock</code> and the message passes through unchanged.</p></details></li>
<li data-answer="2"><p class="quiz-q">The stock API is down, and post-order's error handler is On Error Continue with a Set Payload. What does the client get?</p><ul class="quiz-options"><li>A 500 with the error's description</li><li>No response at all</li><li>A 200 with the handler's payload</li></ul><details class="quiz-why" id="quiz-why-2"><summary>Answer</summary><p>A 200 with the handler's payload. On Error Continue ends the flow as a success, so the listener answers as if nothing failed.</p></details></li>
<li data-answer="0"><p class="quiz-q">post-order calls check-stock with a Flow Reference. check-stock is a sub-flow, and the stock call inside it fails. Which error handler runs?</p><ul class="quiz-options"><li>post-order's error handler</li><li>check-stock's own error handler</li><li>None: sub-flows ignore errors</li></ul><details class="quiz-why" id="quiz-why-3"><summary>Answer</summary><p>post-order's. A sub-flow has no error handler: its processors run in place of the Flow Reference, so the error goes to the caller's handler, or to a Try scope around the Flow Reference.</p></details></li>
</ol>

## Where the facts come from

Every claim in this lesson was checked against the Mule 4 documentation:

- [Mule app development](https://docs.mulesoft.com/mule-runtime/latest/mule-app-dev) and [global elements](https://docs.mulesoft.com/mule-runtime/latest/global-elements)
- [The Mule event](https://docs.mulesoft.com/mule-runtime/latest/about-mule-event) and [target variables](https://docs.mulesoft.com/mule-runtime/latest/target-variables)
- [Error handling](https://docs.mulesoft.com/mule-runtime/latest/error-handling), [On-Error scopes](https://docs.mulesoft.com/mule-runtime/latest/on-error-scope-concept) and the [HTTP Listener reference](https://docs.mulesoft.com/http-connector/latest/http-listener-ref)
- [Flows and sub-flows](https://docs.mulesoft.com/mule-runtime/latest/about-flows) and [Flow Reference](https://docs.mulesoft.com/mule-runtime/latest/flowref-about)
- [Choice](https://docs.mulesoft.com/mule-runtime/latest/choice-router-concept), [Scatter-Gather](https://docs.mulesoft.com/mule-runtime/latest/scatter-gather-concept) and [For Each](https://docs.mulesoft.com/mule-runtime/latest/for-each-scope-concept)
- [Deployment options](https://docs.mulesoft.com/mule-runtime/latest/deploying)

That's the base the rest of the track builds on. Next up is watermarks: how a polling flow remembers where it stopped. If a lab does something Mule wouldn't, tell me. The contact links are right below.
