12 weighted sections across 4 conceptual layers. ⚑ High-stakes sections account for ~65% of the exam. Hover any domain to see key concepts and illuminate related sections.
One event flows through the flow, mutating as it goes. The key to the exam is understanding what is inside what, who can change it, and what survives a scope boundary.
payload = request body
attributes = headers, URI/query params, method
vars = empty initially
payload = null · attributes = minimal
No incoming message — time-driven only
payload = message / file content
attributes = source metadata
inboundProperties · outboundProperties
flowVars · sessionVars
attributes.'http.uri.params'.x
message.payload
payload → response body
attributes → response attrs
vars → PRESERVED ✓ always
payload → Java List of Maps
No rows → [] not null, not false
Needs Transform Message for JSON output
Fire-and-forget. payload unchanged.
Publish-Consume → payload = reply
target="varName" → result in vars.varName, original payload PRESERVED
Input must be Array — Map/Object = zero iterations. Each iteration: element is payload. After scope: original payload restored. Vars set inside persist outside.
Sends entire event to ALL routes in parallel. Output: Object (not Array) with keys "0","1","2". Access: payload[0].payload. After: attributes = null.
Same Mule event. payload + attributes + vars all travel both directions. No network boundary — internal only.
Sub-flow: always uses calling flow's error handler — no own handler.
Private flow: can have its own handler.
| Boundary Type | payload | attributes | vars | Key Rule |
|---|---|---|---|---|
| Flow Reference (internal) | ✓ | ✓ | ✓ | Same Mule event object. Full bidirectional visibility. |
| HTTP Request (network) | REPLACED → response body |
REPLACED → response attrs |
✓ | vars are the ONLY element that crosses a network boundary intact. |
| For Each scope | → element restored after |
✓ | ✓ set inside persist outside | Input must be Array. Sequential, not parallel. |
| Batch Step (per record) | → record | ✗ | ✗ don't reach On Complete | On Complete = BatchJobResult summary, NOT records. Parallel. |
| Sub-flow (via Flow Ref) | ✓ | ✓ | ✓ | Same as Flow Reference. Sub-flow ALWAYS uses calling flow's error handler. |
Four routers with fundamentally different behaviors. Scatter-Gather's Object output structure is the single most-missed concept. Choice Router expression syntax trips on single-equals and Mule 3 patterns.
| Router | Routes executed | Execution model | Output / result |
|---|---|---|---|
| Choice Router | ONE — first matching condition | Top → bottom, first match wins | Payload from matched route. Default runs when NO condition matches. |
| Scatter-Gather | ALL — always, no exceptions | Parallel | Object keys "0","1","2"… Each value = full Mule message. attributes = null after. |
| First Successful | ONE — first that doesn't throw | Sequential, stops on first success | Result of the first route that completes without error. Remaining routes skipped. |
| Round Robin | ONE — rotates per message | Each new message → next route in sequence | Load distribution across identical processors. Stateful rotation. |
String keys "0", "1", "2"… one per route. Each value is a full Mule message with .payload + .attributes.
Access: payload[0].payload, payload[1].payload
Not split. Every route receives the same complete Mule event simultaneously. SG waits for ALL routes to finish before continuing.
attributes = null · payload = assembled Object · vars from before are still accessible
Wrap each route in a Try scope with On Error Continue → Transform []. A failed route returns an empty array instead of failing the whole Scatter-Gather.
Like firewall rules. Only ONE branch executes. Default branch runs when NO condition matches — it is not an error fallback.
Evaluates a DataWeave boolean.
TRUE → event passes through unchanged.
FALSE → throws error with configurable type (e.g. APP:INVALID_DESTINATION)
Does NOT return a value or modify the event on success.
On failure it throws — the flow's error handler catches it. On success the flow continues as if Validate wasn't there.
Invalid enum value → APIKIT:BAD_REQUEST thrown at the APIKit Router. The Choice Router's when-expression is never evaluated.
For Each and Batch share a surface similarity but differ completely in execution model, scoping, and error behavior. On Complete receiving a summary report (never records), and Max Failed Records = 0 defaulting to zero-tolerance, are the persistent exam traps.
| Feature | For Each | Batch Job |
|---|---|---|
| Execution | Sequential — array order, one at a time | Parallel — records processed concurrently |
| Input type required | Array only — Object/Map = zero iterations, no error thrown | Any collection — auto-split into individual records |
| Payload inside scope | Current array element | Current record (per batch step) |
| Payload after scope | Restored to pre-scope payload | N/A — async handoff; outer payload unaffected |
| Variables set inside | Accessible outside (last iteration value) | Don't propagate to On Complete or outer flow |
| Error behavior | Stops entire scope on first error | Record marked failed; others continue (see Max Failed Records) |
counter variable | Available inside; removed after scope exits | N/A |
| Use case | Small collections, synchronous, in-memory | Large datasets, files, DB result sets — high volume async |
BatchJobResult — a summary report (counts, job ID).Default 0 = zero failures allowed. If ANY record fails, the entire batch stops. This is the opposite of what "zero" implies intuitively.
Batch is asynchronous. Send a 202 Accepted response before the async handoff. On Complete fires long after the client connection has closed.
Passing an Object/Map produces zero iterations with no error. The scope is silently skipped. Input must be an Array.
Fires on schedule regardless of whether the prior execution is still running. Does not wait.
Object Store persists across executions — exactly what watermarking needs to track the last processed ID between Scheduler runs.
Whatever For Each was iterating, the original payload is restored once the scope exits — regardless of what Set Payload calls happened inside. Variables set in the last iteration are accessible outside.
Highest-weighted section. Every trap below has appeared on exams. fun keyword, :: module separator, @() XML attributes, and operation chaining order are the persistent failure points.
%dw 2.0Version declaration — always first line.
output application/jsonOutput MIME type — controls serialization. Default between processors is application/java.
---Separator between header and body. Body is the single expression producing the output.
return()| Operator | Returns | Use |
|---|---|---|
| map | ARRAY | Transform each element of an Array |
| filter | ARRAY | Keep elements matching condition |
| flatMap | ARRAY | map then flatten one level |
| orderBy | ARRAY | Sort Array or Object by key |
| pluck | ARRAY | Transform Object entries → Array |
| groupBy | OBJECT | Group Array by key — keys = group values |
| reduce | ANY | Accumulate Array to single value |
| ++ | SAME TYPE | Concat Strings/Arrays/Objects (types must match) |
groupBy first = wrong because it returns an Object, which filter/orderBy cannot process.
++ type ruleConcatenates Strings, Arrays, or Objects — but types must match. String ++ Object = type error at runtime.
| If you see… | The correct answer is… |
|---|---|
fun keyword | fun · assignment with = · no return keyword · types always Capitalized |
| Module import path separator | :: double colon — both in import path and call syntax (modules::Utility, Utility::fn()) |
| XML attribute wrapper | @() · comma between attrs · element value is null, not empty string |
| String concatenation | ++ not + · String ++ Object throws type error |
groupBy return type | Object — keys are group values, values are Arrays. Never an Array itself. |
filter / map return type | Array — always, both of them |
| Type coercion syntax | as String · as Number · always Capitalized — never as string |
| Format constraint syntax | Curly braces: as String { format: ".##" } — never parentheses |
| Operation chaining order | filter → orderBy → groupBy — wrong order gives wrong result or type error |
typeOf() on Set Payload literal | Always returns String regardless of what the content looks like |
| Custom type definition keyword | type MyName = BaseType { constraint } · not typedef, not colon |
The most conceptually tricky section. On Error Continue does not resume execution after the failing processor. The global handler bypass rule catches everyone. HTTP:* wildcard doesn't work in the Type field.
"Continue" means the handler runs and the flow exits normally. It does NOT mean execution resumes at the processor after the failing one. Those processors never run.
Even if you Set Payload inside the handler, the HTTP Listener returns the original error message — not the Set Payload value.
| Type Field | When Field | What it catches |
|---|---|---|
HTTP:NOT_FOUND | — | Specific error type only |
ANY | — | Everything — use as the last handler |
| (empty) | #[error.errorType.namespace == "HTTP"] | All HTTP:* — the only valid wildcard pattern |
✗ HTTP:* | — | Does NOT work — wildcard invalid in Type field |
Multiple handlers: top to bottom, first match fires, rest ignored. Put specific types first, ANY last.
Mule default handler fires automatically — always present. Behaves like On Error Propagate: logs error, returns 500.
Sub-flows always use the calling flow's error handler — they cannot have their own. Private flows can have their own. Heavily tested distinction.
| If you see… | The answer is… |
|---|---|
| On Error Continue — does flow resume after error? | No. Handler runs, its output = flow result, flow exits normally. Subsequent processors never execute. |
| On Error Propagate — what does HTTP Listener return? | Original error message — even if Set Payload ran in the handler. |
| Flow has own handler but no scope matches the error | Global handler bypassed. Error propagates unhandled → HTTP Listener → 500. |
| No error handler configured at all | Mule default handler fires automatically — always present. Behaves like Propagate → 500. |
HTTP:* wildcard in Type field | Does NOT work. Use: Type = (empty) + When = #[error.errorType.namespace == "HTTP"] |
| Child private flow — On Error Continue catches error | Calling flow never sees the error. Gets handler's payload as Flow Reference result. Continues normally. |
| Child private flow — On Error Propagate | Error bubbles up to calling flow's error handler. |
| Sub-flow error handler | Sub-flows never have their own. Always uses calling flow's handler. |
| Try scope — On Error Continue inside | Flow continues after the Try scope — the only scenario where flow continues after an error. |
| Global handler location config | Must be a global element — not pom.xml, not properties file. |