9.2 Scatter-Gather Router: Parallel Execution, Aggregation & Error Traps

Key Takeaways

  • The Scatter-Gather router (<scatter-gather>) broadcasts an identical clone of the incoming Mule event to two or more parallel processing routes simultaneously on separate threads.
  • Each parallel route executes in complete isolation; modifications made to the payload or variables in one route are invisible to other routes during execution.
  • Upon successful completion of all routes, Scatter-Gather aggregates the results into a composite Mule event where payload is a Map keyed by route index string ("0", "1", "2", ...), with each entry containing the respective route's MuleMessage (payload and attributes).
  • Variables created or modified in individual routes are merged back into the main flow context upon completion; if multiple routes modify the same variable name, the resulting value is non-deterministic.
  • If an unhandled error occurs in ANY route, Scatter-Gather immediately halts normal aggregation and raises a MULE:COMPOSITE_ROUTING error containing an error.childErrors collection of all failures and partial successes.
Last updated: August 2026

Scatter-Gather Router: Parallel Execution, Aggregation & Error Traps

Aggregating information from multiple disparate backends—such as querying airline, hotel, and car rental services simultaneously—is a foundational integration requirement. In Mule 4, the Scatter-Gather Router (<scatter-gather>) enables concurrent, parallel processing across multiple routes, aggregating individual responses into a single composite Mule message. Understanding thread dispatching, event cloning, result aggregation, variable merging, and composite error handling is essential for mastering flow control in Mule applications.


1. Scatter-Gather Execution Architecture & Event Cloning

When a Mule event enters a <scatter-gather> router, the runtime performs the following sequence:

  1. Event Cloning: A separate, deep copy of the incoming Mule event (including payload, attributes, and vars) is created for every declared <route>.
  2. Parallel Dispatch: Each route is assigned to an independent thread from the Mule runtime reactive thread pool and executed concurrently.
  3. Thread Synchronization: The router waits for all parallel routes to complete (or for a timeout or unhandled exception to occur) before proceeding.
+-------------------------------------------------------------------------------------------------+
|                              SCATTER-GATHER PARALLEL ARCHITECTURE                               |
|                                                                                                 |
|                                  [ Incoming Mule Event ]                                        |
|                            payload: { "tripId": "T-101" }                                       |
|                            vars:    { "userId": "U-88" }                                        |
|                                             |                                                   |
|                                             v                                                   |
|                           +-----------------------------------+                                 |
|                           |      <scatter-gather> Router      |                                 |
|                           +-----------------------------------+                                 |
|                             /               |               \                                   |
|          [Clone Event Copy 0]      [Clone Event Copy 1]      [Clone Event Copy 2]               |
|                    |                        |                        |                          |
|                    v                        v                        v                          |
|         +---------------------+  +---------------------+  +---------------------+               |
|         | Route 0 (Thread A)  |  | Route 1 (Thread B)  |  | Route 2 (Thread C)  |               |
|         | <http:request>      |  | <http:request>      |  | <http:request>      |               |
|         | Flight Partner API  |  | Hotel Partner API   |  | Car Rental API      |               |
|         +---------------------+  +---------------------+  +---------------------+               |
|                    |                        |                        |                          |
|                    v                        v                        v                          |
|           [Flight Message]             [Hotel Message]          [Car Rental Msg]                |
|                    \                        |                        /                          |
|                     \                       |                       /                           |
|                      v                      v                      v                            |
|                           +-----------------------------------+                                 |
|                           |       AGGREGATION ENGINE          |                                 |
|                           +-----------------------------------+                                 |
|                                             |                                                   |
|                                             v                                                   |
|                                [ Composite Mule Event ]                                         |
|          payload: {                                                                             |
|            "0": { attributes: { statusCode: 200 }, payload: { flights: [...] } },              |
|            "1": { attributes: { statusCode: 200 }, payload: { hotels: [...] } },               |
|            "2": { attributes: { statusCode: 200 }, payload: { cars: [...] } }                  |
|          }                                                                                      |
|          vars: { userId: "U-88", (merged variables from all routes) }                           |
+-------------------------------------------------------------------------------------------------+

XML Configuration Example:

<flow name="aggregate-travel-itinerary-flow" doc:name="Aggregate Travel Itinerary Flow">
    <http:listener config-ref="HTTP_Listener_config" path="/itinerary" doc:name="Listener"/>
    
    <set-variable variableName="bookingId" value="#[payload.bookingId]" doc:name="Set Booking ID"/>
    
    <scatter-gather doc:name="Scatter-Gather: Aggregate Services" timeout="10000">
        <!-- Route 0: Flights API -->
        <route>
            <http:request config-ref="Flights_HTTP_Config" path="/flights" method="GET" doc:name="Get Flights">
                <http:query-params><![CDATA[#[{'bookingId': vars.bookingId}]]]></http:query-params>
            </http:request>
            <set-variable variableName="flightsStatus" value="SUCCESS" doc:name="Set Flights Status"/>
        </route>
        
        <!-- Route 1: Hotels API -->
        <route>
            <http:request config-ref="Hotels_HTTP_Config" path="/hotels" method="GET" doc:name="Get Hotels">
                <http:query-params><![CDATA[#[{'bookingId': vars.bookingId}]]]></http:query-params>
            </http:request>
            <set-variable variableName="hotelsStatus" value="SUCCESS" doc:name="Set Hotels Status"/>
        </route>
        
        <!-- Route 2: Rental Cars API -->
        <route>
            <http:request config-ref="Cars_HTTP_Config" path="/cars" method="GET" doc:name="Get Cars">
                <http:query-params><![CDATA[#[{'bookingId': vars.bookingId}]]]></http:query-params>
            </http:request>
            <set-variable variableName="carsStatus" value="SUCCESS" doc:name="Set Cars Status"/>
        </route>
    </scatter-gather>
    
    <!-- Transform the aggregated composite payload into a unified response -->
    <ee:transform doc:name="Aggregate Response">
        <ee:message>
            <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
    bookingId: vars.bookingId,
    flights: payload["0"].payload,
    hotels: payload["1"].payload,
    cars: payload["2"].payload,
    flightResponseCode: payload["0"].attributes.statusCode
}]]></ee:set-payload>
        </ee:message>
    </ee:transform>
</flow>

2. Structure of the Aggregated Composite Message

When all routes finish successfully, the resulting payload is an Object / Map containing zero-based string indices ("0", "1", "2", ...) corresponding to the declaration order of the <route> elements.

Detailed Structure of payload After Scatter-Gather:

{
  "0": {
    "attributes": {
      "statusCode": 200,
      "headers": { "content-type": "application/json" }
    },
    "payload": {
      "airline": "United",
      "flightNumber": "UA-442",
      "price": 350.00
    }
  },
  "1": {
    "attributes": {
      "statusCode": 200,
      "headers": { "content-type": "application/json" }
    },
    "payload": {
      "hotelName": "Grand Hyatt",
      "nights": 3,
      "totalRate": 620.00
    }
  }
}

DataWeave Access Patterns:

  • Direct Indexed Navigation: payload["0"].payload accesses Route 0's payload body; payload["0"].attributes.statusCode accesses Route 0's attributes.
  • Dynamic Pluck Extraction: payload pluck ((routeMessage) -> routeMessage.payload) converts all route payloads into a flat JSON array [ { flight... }, { hotel... } ].
  • Descendant Extraction (..): payload..price extracts all fields named price across all child route payloads.

[!IMPORTANT] payload["0"].payload vs payload[0] In Mule 4, the output of a successful Scatter-Gather is a Map of MuleMessage objects, NOT a simple JSON Array. Accessing payload[0] treats the payload as an indexed array and will fail or return null in DataWeave. You must access the route key as a map index: payload["0"].payload or payload."0".payload.


3. Variable Merging Rules & Collisions

How variables behave across parallel Scatter-Gather routes is a major certification focus:

+-----------------------------------------------------------------------------------------+
|                         VARIABLE MERGING IN SCATTER-GATHER                              |
|                                                                                         |
|   BEFORE SCATTER-GATHER:                                                                |
|     vars.sharedVar = "ORIGINAL"                                                         |
|                                                                                         |
|   ROUTE 0 MODIFICATIONS:             ROUTE 1 MODIFICATIONS:                             |
|     vars.sharedVar = "FROM_ROUTE_0"    vars.sharedVar = "FROM_ROUTE_1"                  |
|     vars.varRoute0 = "DATA_0"          vars.varRoute1 = "DATA_1"                        |
|                                                                                         |
|   AFTER SCATTER-GATHER (MERGED):                                                        |
|     vars.varRoute0  = "DATA_0"       (Cleanly merged)                                   |
|     vars.varRoute1  = "DATA_1"       (Cleanly merged)                                   |
|     vars.sharedVar  = NON-DETERMINISTIC! (Either "FROM_ROUTE_0" or "FROM_ROUTE_1")     |
+-----------------------------------------------------------------------------------------+

Variable Rules Summary:

  1. New Unique Variables: Any variable created in an individual route (e.g., vars.flightId in Route 0, vars.hotelId in Route 1) is cleanly merged into the parent flow's variable context and is available downstream.
  2. Variable Collisions: If multiple parallel routes modify the same variable name (or modify an existing variable initialized before the router), the final value is non-deterministic (last thread to complete overwrites the value).
  3. Best Practice: Always use route-prefixed or uniquely named variables (e.g., vars.route0_result, vars.route1_result) inside parallel routes to eliminate collision risks.

4. Error Handling: MULE:COMPOSITE_ROUTING & Route Isolation

If ANY single route throws an unhandled error during execution:

  1. Scatter-Gather immediately cancels normal aggregation.
  2. Scatter-Gather raises a MULE:COMPOSITE_ROUTING error.
  3. Execution jumps immediately to the enclosing flow's error handler.
+-----------------------------------------------------------------------------------------+
|                        SCATTER-GATHER COMPOSITE ERROR SCENARIO                          |
|                                                                                         |
|   Route 0: <http:request> ---> SUCCESS (HTTP 200 Flight Data)                           |
|   Route 1: <http:request> ---> ERROR: HTTP:CONNECTIVITY (Hotel Service Unreachable!)    |
|   Route 2: <http:request> ---> SUCCESS (HTTP 200 Car Data)                              |
|                                                                                         |
|   RESULT: Entire Scatter-Gather throws MULE:COMPOSITE_ROUTING                            |
|                                                                                         |
|   IN ERROR HANDLER:                                                                     |
|   - error.description: Summary of composite failure                                     |
|   - error.childErrors: Map containing error objects from failed routes                  |
|       error.childErrors["1"].errorType    ===> HTTP:CONNECTIVITY                        |
|       error.childErrors["1"].description  ===> "HTTP GET failed: Connection refused"    |
|   - error.errorMessage.payload: Map containing both successes and errors                |
+-----------------------------------------------------------------------------------------+

Route-Level Error Isolation Pattern (Using <try>)

In many business scenarios, a failure in one non-critical route (such as rental cars) should not abort the entire aggregation. To achieve fault tolerance, wrap the route's processors inside a <try> scope with an <on-error-continue> error handler:

<scatter-gather doc:name="Fault-Tolerant Scatter-Gather">
    <!-- Route 0: Flight Service -->
    <route>
        <http:request config-ref="Flights_HTTP_Config" path="/flights" method="GET" doc:name="Get Flights"/>
    </route>
    
    <!-- Route 1: Hotel Service with Try-Catch Isolation -->
    <route>
        <try doc:name="Try Hotel Lookup">
            <http:request config-ref="Hotels_HTTP_Config" path="/hotels" method="GET" doc:name="Get Hotels"/>
            <error-handler>
                <on-error-continue type="ANY" doc:name="Catch Hotel Failure">
                    <logger level="WARN" message="#['Hotel lookup failed: ' ++ error.description]" doc:name="Log Warn"/>
                    <!-- Set a fallback empty payload so Route 1 succeeds -->
                    <set-payload value="#[{ 'hotels': [], 'status': 'UNAVAILABLE' }]" doc:name="Fallback Payload"/>
                </on-error-continue>
            </error-handler>
        </try>
    </route>
</scatter-gather>

[!TIP] How <on-error-continue> Protects Scatter-Gather Because <on-error-continue> catches the error inside Route 1 and returns a fallback payload, Route 1 completes successfully from the perspective of the Scatter-Gather router. Scatter-Gather aggregates the fallback payload into payload["1"].payload without throwing a MULE:COMPOSITE_ROUTING exception.


5. Exam Watch: Core Scatter-Gather Scenarios

[!IMPORTANT] Composite Error Structure When a route fails, the overall error type is always MULE:COMPOSITE_ROUTING. Individual route failures are inspected using error.childErrors["0"], error.childErrors["1"], etc.

[!WARNING] Concurrency & Thread Safety Parallel routes execute concurrently on separate threads. Never perform non-thread-safe modifications to external shared resources (like writing to the same local file or updating a shared static cache without locks) from inside parallel routes.

[!TIP] Timeout Attribute The timeout attribute on <scatter-gather timeout="5000"> is defined in milliseconds. If any route takes longer than the timeout to complete, Scatter-Gather aborts and raises a MULE:COMPOSITE_ROUTING containing a MULE:TIMEOUT child error.

Test Your Knowledge

A Mule flow contains a Scatter-Gather router with two parallel routes. Route 0 invokes an Accounts REST API returning { "accountId": 101, "name": "Acme Corp" }. Route 1 invokes a Billing REST API returning { "balance": 450.00, "status": "CURRENT" }. Both routes complete successfully with HTTP 200 status codes. How should a developer configure a Transform Message component immediately following the Scatter-Gather to extract the account name and balance into a unified JSON object?

A
B
C
D
Test Your Knowledge

Before entering a Scatter-Gather router, a flow variable vars.orderStatus is initialized to "PENDING". Route 0 executes and sets vars.orderStatus = "APPROVED" and vars.route0Result = "FLIGHT_OK". Route 1 executes concurrently and sets vars.orderStatus = "REJECTED" and vars.route1Result = "HOTEL_OK". Both routes complete without error. What is the state of the flow variables immediately after exiting the Scatter-Gather router?

A
B
C
D
Test Your Knowledge

A Scatter-Gather router contains three parallel routes. Route 0 and Route 2 complete successfully with HTTP 200 responses. Route 1 throws an unhandled HTTP:CONNECTIVITY error because the backend service is offline. What error is raised by the Scatter-Gather router, and how can the developer inspect the specific failure details of Route 1 in the flow's error handler?

A
B
C
D
Test Your Knowledge

A developer is designing a flight booking application that calls three independent airline supplier APIs in parallel using a Scatter-Gather router. The business requirement states that if any single airline API is unavailable or returns an error, the flow must NOT fail; instead, it should aggregate the results from the remaining available airlines and return an empty list for the failing supplier. What configuration achieves this requirement?

A
B
C
D