--- name: write-microflows description: "Microflow syntax reference in MDL — every activity type, control flow, expressions, and the mistakes that fail `mxcli check`. Use before writing any CREATE MICROFLOW, and when debugging a microflow syntax error." --- # Mendix Microflow Skill ## Reference files `SKILL.md` covers the shape of a microflow and the decisions. The detail lives beside it: - [`reference/control-flow.md`](reference/control-flow.md) — every form of `if`, `case`, `split type`, `loop` and `while`, plus error handling (`try`/custom handlers) and the `@position` / `@merge` / `@anchor` activity annotations. - [`reference/data-operations.md`](reference/data-operations.md) — create, change, commit, delete; list operations and aggregates; `retrieve` from database, association and list; XPath navigation. - [`reference/integration.md`](reference/integration.md) — `rest call` and `send rest request`, legacy SOAP, calling Java actions (including the `empty` argument and microflow-typed parameters), `execute database query`, file downloads. - [`reference/pitfalls.md`](reference/pitfalls.md) — **read this when `mxcli check` rejects something you believe is correct.** The anti-patterns, the CE0111 duplicate-variable trap, the syntax that looks plausible and does not parse, and the Studio Pro errors each one produces. ## When to Use This Skill Use this skill when: - Writing CREATE MICROFLOW statements - Debugging microflow syntax errors - Converting Studio Pro microflows to MDL - Understanding microflow control flow and structure If you're not sure whether the logic belongs in a microflow or a nanoflow, read the next section first. The mirror lives in [write-nanoflows](../write-nanoflows/SKILL.md) — keep both copies in sync. ## Changing an Existing Microflow Choose the mode by who owns the microflow ([choose-edit-mode](../choose-edit-mode/SKILL.md)): - **Created by your MDL scripts, and not edited in Studio Pro since:** edit the script (or fresh `describe` output) and re-run `create or modify`. - **Authored in Studio Pro:** prefer `alter microflow X { insert/replace/drop … }` (targets from `describe microflow X with handles`). `create or modify` of `describe` output patches too: unchanged writes nothing; a statement (even one whose handler returns), guard clause, `return` value, `if` condition, header clause, parameter (added/retyped; removed only if unused) or stated `@position`/`@start` change is patched in place (a move keeps the node's flows). A redrawn `@anchor`/`@curve`, loop body, error handler or other `return` added/taken away rebuilds under mdl 0 (`MDL-V1-REBUILD`: IDs renumbered, merges and curves lost) and is refused under `mdl 1;`. **To change a loop body, `alter … replace` the whole loop** — neither mode edits inside one ([pitfalls](reference/pitfalls.md#11-changing-something-inside-a-loop-body)). **To rebuild deliberately, `drop` and `create` in ONE script** — the grants carry only within it ([pitfalls](reference/pitfalls.md#drop--create-is-still-a-new-document)). ## When to Use a Microflow vs a Nanoflow | Scenario | Use | |----------|-----| | Querying the database | Microflow | | Calling REST services or external actions, or sending email (`send email`, 11.13+) | Microflow | | Running Java actions | Microflow | | File generation or download | Microflow | | Transactional commits (rollback on error) | Microflow | | Background scheduled logic | Microflow | | Client-side form validation before save | Nanoflow | | UI navigation and page routing | Nanoflow | | Calling device features (GPS, phone, camera) | Nanoflow | | Offline data access and local storage | Nanoflow | | Calling JavaScript actions (NanoflowCommons) | Nanoflow | | Showing progress indicators / confirmation dialogs | Nanoflow | **Rule of thumb:** A nanoflow runs before the server call. A microflow IS the server call. ## Key Differences from Nanoflows | Aspect | Microflow | Nanoflow | |--------|-----------|----------| | **Execution** | Server-side | Client-side (browser/mobile) | | **Database access** | Full | No direct access | | **Transactions** | Supported | Not supported | | **Java actions** | Supported | Not supported | | **JavaScript actions** | Not supported | Supported | | **SYNCHRONIZE** | Not available | Available (offline sync) | | **File downloads** | Supported | Not supported | | **Error handling** | Full `ON ERROR` blocks; `RAISE ERROR` **inside a handler only** (main flow = MDL084 / CE0710) | Per-action `ON ERROR` supported; `RAISE ERROR` / `ErrorEvent` forbidden | | **Offline** | Not available | Available | | **Binary return type** | Supported | Not supported | For nanoflow-specific authoring guidance, see [write-nanoflows](../write-nanoflows/SKILL.md). ## Microflow Structure **CRITICAL: All microflows MUST have JavaDoc-style documentation** ```mdl /** * Microflow description explaining what it does * * Detailed explanation of the business logic, use cases, * and any important implementation notes. * * @param $Parameter1 Description of first parameter * @param $Parameter2 Description of second parameter * @returns Description of return value * @since 1.0.0 * @author Team Name */ create microflow Module.MicroflowName ( $Parameter1: type, $Parameter2: type ) returns ReturnType as $ReturnVariable [folder 'FolderPath'] begin -- Microflow logic here return $ReturnVariable; end; ``` ### `exposed as … action` — putting a microflow in the toolbox Studio Pro can show a microflow in its toolbox, so whoever drags it onto a flow does not need to know it is a microflow rather than a Java action. A microflow has **two** such entries — one for the microflow editor, one for the workflow editor — so the clause names which: ```mdl mdl 1; create microflow Module.FormatCode ($Raw: String) returns String exposed as microflow action 'Format code' in 'Toolbox demo' icon 'assets/format-64.png' icon dark 'assets/format-64-dark.png' image 'assets/format-256.png' exposed as workflow action 'Format code' in 'Toolbox demo' begin return trim($Raw); end; ``` The icon should be a **64x64** PNG and the image **256x192**; paths resolve against the directory of the .mdl file, so a script and its artwork travel together. A different size is written with a warning; a file that is not a PNG is refused. **An omitted clause preserves what is stored — it does not clear it:** | You write | What happens | |---|---| | `exposed as microflow action 'X' in 'Y'` | Caption and category from MDL; **icon and image carried** from what was stored | | *(no clause)* | Both entries **preserved**, bitmaps included | | `not exposed as workflow action` | Removes that one entry; the microflow entry is untouched | | `… drop icon dark` | Clears one bitmap; the other three are untouched | Rewriting a microflow's body must not cost it the icon a designer set in Studio Pro, and saying nothing about the toolbox is not asking to be taken out of it. Nothing below Studio Pro sees the difference — `mx check` reports 0 errors either way — so the preserve rule is the only thing protecting it. **Nanoflows and rules cannot be exposed.** Only `Microflows$Microflow` stores a toolbox entry; the clause is refused on the other two with a message naming the alternative. Java and JavaScript actions have one entry each and use the shorter `exposed as 'Caption' in 'Category'` — see the `java-actions` skill. ### `@excluded` — documents excluded from the project `@excluded` before a `create microflow` marks the document **"Exclude from project"** (the same checkbox Studio Pro offers). The document stays in the `.mpr`, does not build, and `list microflows` reports it in the `Excluded` column. ```mdl mdl 1; @excluded create microflow MyModule.LegacyCalc () returns Integer begin return 7; end; ``` The sibling document annotation is **`@applyentityaccess`** — runs the flow under the current user's entity access rules rather than with full access, with the same absent-preserves rule and an explicit `(false)` to turn it off ([pitfalls](reference/pitfalls.md#apply-entity-access)). Two rules follow, and both are enforced rather than documented-and-hoped: - **An absent `@excluded` never un-excludes.** It means "the script does not say", not "make this active" — so re-running a `create or modify` you wrote before the document was excluded leaves the exclusion alone. Un-exclude in Studio Pro. (Before #914 the rewrite cleared it, which is how a valid project ended up failing **CE0122** — see the next rule.) - **A name is not unique when a twin is excluded.** Mendix allows two documents of the same name in one module as long as at most one is active — verified on 11.13.0: the excluded pair builds at 0 errors, the same pair both active is `[error] CE0122 "Duplicate document name"`. `create or modify`, `describe` and the other by-name lookups therefore target the **live** document; the excluded twin is neither rewritten nor deleted. The same applies to every document type that carries the flag — nanoflows, pages, snippets, enumerations, queues, workflows, Java/JavaScript actions, mappings, JSON structures, REST/OData services, image collections and the agent documents. ### FOLDER Option Place microflows in folders for organization: ```mdl mdl 1; create microflow MyModule.ACT_ProcessOrder ($Order: MyModule.Order) returns boolean as $success folder 'Orders/Processing' begin -- logic return true; end; ``` **Key Rules:** - Parameters start with `$` prefix - Return variable must be declared or used - Every microflow must end with `return` statement - Every body statement ends with a semicolon `;` — **required**, not optional. This includes block terminators: `end if;`, `end loop;`, `end while;`, `end case;`. A missing one is a parse error (`missing ';' at 'return'`), not a warning. - Microflow ends with `/` separator ### Parameter Types ```mdl -- Primitive types $Name: string $count: integer $Amount: decimal $IsActive: boolean $date: datetime -- Entity types $Customer: Module.Entity -- List types $ProductList: list of Module.Product -- Enumeration types $status: enum Module.OrderStatus ``` ## Variable Declarations ### ✅ CORRECT Syntax ```mdl -- Primitive types with initialization declare $Counter integer = 0; declare $message string = 'Hello'; declare $IsValid boolean = true; declare $Today datetime = [%CurrentDateTime%]; declare $status Enumeration(Module.OrderStatus) = Module.OrderStatus.Open; ``` > **You cannot `declare` an object (entity) variable.** `declare` becomes a > *Create Variable* activity, which Mendix only allows to hold **primitive** types > (String, Integer/Long, Decimal, Boolean, DateTime, Enumeration). An object type > is rejected by Studio Pro/mxbuild with CE0053 ("Selected type is not allowed"), > plus CE0038 ("Value required") and CE7247 on any following `set` — whether or > not you give it an initializer. `mxcli check` now flags it as **MDL043**. There > is **no** "empty object variable" activity. Get objects from one of these: > - a microflow **parameter**: `create microflow M.Save ($Product: Test.Product) ...` > - a **retrieve**: `retrieve $Product from Test.Product where Code = $c first;` > - a **create object**: `$Product = create Test.Product (Name = $n);` > - a **loop iterator**: `loop $Product in $Products ...` > **You cannot `declare` a list either.** Same *Create Variable* restriction — > Studio Pro rejects a list with CE0053/CE0038, and `mxcli check` flags it as > **MDL040**. Get lists from: > - a microflow **parameter**: `create microflow M.Process ($Items: list of Test.Product) ...` > - a **retrieve**: `retrieve $Products from Test.Product where IsActive = true;` > - a **create list**: `$Products = create list of Test.Product;` > **Decimal values into an `integer`/`long` fail CE0117.** Integer division > (`$a div $b`) is always a Decimal even for two integers, and so are `random()` > and the duration `*Between` functions (`secondsBetween`, `minutesBetween`, > `hoursBetween`, `daysBetween`, `weeksBetween`). Assigning any of them straight > to an `integer`/`long` variable fails `mx check` with **CE0117** (`mxcli check` > now flags it as **MDL041**). Either declare the target `decimal`, or round it: > ```mdl > declare $Avg decimal = $Total div $Count; -- ✅ Decimal target > declare $Whole integer = round($Total div $Count); -- ✅ rounded to Integer > declare $Secs integer = round(secondsBetween($a,$b)); -- ✅ rounded to Integer > declare $Bad integer = $Total div $Count; -- ❌ CE0117 / MDL041 > ``` > The `calendar*Between` functions (`calendarMonthsBetween`, `calendarYearsBetween`) > return whole units (Integer) and are fine to assign directly. ### Changing a variable: always `set` `set $Counter = $Counter + 1;` changes a variable. `$x = …` without `set` is an activity that creates `$x` — required under `mdl 1;` (`MDL-V1-SET` otherwise). List operations and aggregates are one statement per activity and never nest: `$Open = filter $Orders by Status = M.Status.Open;` then `$N = count $Open;` — see [`reference/data-operations.md`](reference/data-operations.md#one-statement-per-activity). ### ❌ INCORRECT Syntax ```mdl -- WRONG: Declaring an object/entity variable (CE0053/CE0038, MDL043) declare $Product Test.Product; -- bare object declare is invalid declare $Product Test.Product = $someObj; -- initialized object declare is also invalid -- WRONG: Declaring a list variable (CE0053/CE0038, MDL040) declare $ProductList list of Test.Product = empty; -- use a parameter, retrieve, or create list -- WRONG: Using AS keyword (not supported in mxcli) declare $Product as Test.Product; -- ERROR: parse error -- WRONG: No value (CE0038, MDL061) declare $X string; -- a Create Variable activity requires a value -- WRONG: Missing type declare $Counter = 0; -- Type inference not always supported -- WRONG: Using 'OF' instead of 'of' declare $list list of Test.Product; -- Case sensitive ``` ## Operators ### Arithmetic ```mdl set $Result = $A + $B; -- Addition set $Result = $A - $B; -- Subtraction set $Result = $A * $B; -- Multiplication set $Result = $A div $B; -- Division (use 'div', not '/') ``` **Important**: Use `div` for division, NOT `/`. In a Mendix expression `/` is the member/association separator (`$obj/Attr`), so `$A / $B` is not division — `mxcli check` rejects it as **MDL045** (it would fail the build with CE0117). Integer/decimal division always yields a Decimal; wrap it in `round()`/`trunc()` for an Integer result (else **MDL041**). ### Comparison ```mdl $A = $B -- Equals $A != $B -- Not equals $A > $B -- Greater than $A >= $B -- Greater than or equal $A < $B -- Less than $A <= $B -- Less than or equal $A = empty -- Check if empty/null $A != empty -- Check if not empty ``` ### Boolean Logic ```mdl set $Result = $A and $B; -- Logical AND set $Result = $A or $B; -- Logical OR set $Result = not $A; -- Logical NOT -- Complex expressions if $IsActive and $IsValid and $HasStock then set $CanProcess = true; end if; ``` ### Date construction `dateTime(...)` / `dateTimeUTC(...)` build a date from **literal numeric constants only** — a variable or computed argument fails the build with CE0117 (`mxcli check` flags it as **MDL046**). To build a date from variables, step off a literal anchor with `addDays()` / `addMonths()` (which *do* take variables): ```mdl -- WRONG: variable args to dateTime() (CE0117 / MDL046) set $D = dateTime(2026, $Month, $Day); -- RIGHT: anchor on a literal, then step with addMonths/addDays set $D = addDays(addMonths(dateTime(2026, 1, 1), $Month - 1), $Day - 1); ``` ## Logging ```mdl -- Log levels log info 'Information message'; log warning 'Warning message'; log error 'Error message'; -- With node name log info node 'OrderService' 'Processing order'; log warning node 'ValidationService' 'Invalid data detected'; -- With variables (use concatenation) log info node 'OrderService' 'Order processed: ' + $OrderNumber; log error node 'Service' 'Error: ' + $ErrorMessage; ``` ## Special Values ```mdl empty -- Null/empty value [%CurrentDateTime%] -- Current date/time [%CurrentUser%] -- Current user object toString($value) -- Convert to string ``` > **No `randomInt`.** Mendix has no `randomInt` function. Use `random()` (returns a > Decimal in [0,1)) and round to an integer range — e.g. a value in 0..8 is > `round(random() * 8)`. Because `random()` is a Decimal, assigning it (or `div`, > `secondsBetween()`, and the other duration `*Between` functions) directly to an > `integer` variable fails the build with CE0117 — wrap it in `round()`/`floor()`/`ceil()`. > `mxcli check` now flags an unknown expression function like `randomInt` as > **MDL044** (with a "did you mean random()?" hint), and a Decimal assigned to an > integer target as **MDL041** — before the build does. > > **MDL044 also blocks `mxcli exec`**, not just `check`: a call to a name Mendix > has no built-in for is CE0117 at build time, so exec refuses to write the > microflow or nanoflow (log messages included). Not real: `currentDeviceType()`, > `[%CurrentDeviceType%]` (a CE0117 `check` misses) and `trunc()` (use > `round`/`floor`/`ceil`). If exec rejects a function you believe IS a Mendix > built-in, build it once and — if mxbuild accepts it — add it to `funcTable` in > `mdl/exprcheck/func_checker.go`; that table is the rule's only allow-list. ## Complete Example ```mdl mdl 1; /** * Process order with validation and status update. * * @param $OrderNumber The order to process * @returns true when the order was found and marked processed */ create microflow Shop.ProcessOrder ( $OrderNumber: string ) returns boolean as $success begin declare $success boolean = false; -- Find the order (the retrieve establishes $Order — objects are never declared) retrieve $Order from Shop.Order where OrderNumber = $OrderNumber; -- Validate order exists if $Order = empty then log warning node 'OrderService' 'Order not found: ' + $OrderNumber; return false; end if; -- Validate customer association if $Order/Shop.Order_Customer = empty then log error node 'OrderService' 'Order has no customer'; return false; end if; -- Update order status change $Order ( status = 'PROCESSING', ProcessedDate = [%CurrentDateTime%]); commit $Order; -- Log success log info node 'OrderService' 'Order processed: ' + $OrderNumber; set $success = true; return $success; end; ``` ## Calling Microflows ### ✅ CORRECT Syntax ```mdl -- Call with result assignment (no SET keyword) $Result = call microflow Module.ProcessOrder(Order = $Order); -- Call without result (void microflow) call microflow Module.SendNotification(message = $message); -- Call with error handling $Result = call microflow Module.ExternalService(data = $data) on error continue; -- Run the call on a task queue (background execution). The clause goes after -- the arguments and before any ON ERROR, and works on CALL JAVA ACTION too. call microflow Module.ACT_Refresh() in queue Module.RefreshQueue; call java action Module.RefreshData(Url = $Url) in queue Module.RefreshQueue; ``` **Queued calls** — the queue must already exist (`create task queue Module.RefreshQueue (Parallelism: 2)`), and the called flow must return nothing: a queued **microflow with a `returns` clause** fails the build with CE7033 (`mxcli check`: MDL088), a queued **Java action must `returns void`** or it fails with CE7038. Rewriting a microflow that has a queued call must restate the `in queue` clause; a rewrite that omits it is refused rather than silently dropping the binding. See `.claude/skills/mendix/scheduled-events-and-queues`. ### ❌ INCORRECT Syntax ```mdl -- WRONG: Do NOT use SET with CALL MICROFLOW set $Result = call microflow Module.ProcessOrder(Order = $Order); -- ERROR! -- CORRECT: Direct variable assignment $Result = call microflow Module.ProcessOrder(Order = $Order); ``` **Important**: The `set` keyword is for changing existing variable values, NOT for capturing microflow return values. Use direct assignment (`$var = call microflow ...`). ### Parameter Name Matching **CRITICAL**: Parameter names in `call microflow` must **exactly match** the parameter names declared in the target microflow's signature (without the `$` prefix). A mismatch causes a build error (MxBuild) but may fail silently at MDL execution time. ```mdl -- Target microflow declaration: create microflow Module.SendEmail ($Recipient: string, $Subject: string) begin ... end; -- CORRECT: parameter names match the declaration call microflow Module.SendEmail(Recipient = $Email, Subject = $title); -- WRONG: parameter name does not match (EmailAddress vs Recipient) call microflow Module.SendEmail(EmailAddress = $Email, Subject = $title); -- BUILD ERROR! ``` When calling microflows, always check the target's parameter list. Use `describe microflow Module.Name` to see the exact parameter names. ## Page Navigation ### SHOW PAGE ```mdl -- Open page with parameter show page Module.EditPage(Product = $Product); ``` Every call site binds an argument as `Param = expression`, with no `$` on the parameter name: `call microflow`, `show page`, and widget actions (`action: show page Module.Page(Param = $value)`) alike. `$Param = $value` and `Param: $value` still parse but are deprecated (MDL-DEPR006/007); `mxcli fmt --upgrade` rewrites them. ### CLOSE PAGE ```mdl close page; ``` ### SHOW HOME PAGE ```mdl show home page; ``` ## Validation Checklist Before executing a microflow script, verify: - [ ] **No object (entity) or list is `declare`d** — objects come from a parameter, retrieve, create object, or loop iterator (MDL043); lists from a parameter, retrieve, or create list (MDL040) - [ ] **All primitive variables are declared before SET** (`declare $var type = value;`) - [ ] XPath association navigation uses qualified names (`Module.AssociationName`) - [ ] All referenced attributes exist in entity definitions - [ ] Every flow path ends with `return` - [ ] No code appears after `return` statements - [ ] Division uses `div` operator (not `/`) - [ ] All entity/association names are fully qualified - [ ] **CALL MICROFLOW parameter names exactly match target signature** (use `describe microflow` to verify) - [ ] Microflow ends with `/` separator - [ ] Parameters start with `$` prefix - [ ] Proper closing for control structures (`end if`, `end loop`) ## Tips for Success 1. **Always use fully qualified names**: `Module.Entity`, `Module.Association` 2. **Test incrementally**: Create simple microflows first, then add complexity 3. **Check entity definitions**: Ensure all attributes exist before referencing 4. **Use meaningful variable names**: `$Customer` not `$c`, `$ProductList` not `$list` 5. **Comment complex logic**: Use `--` for inline comments 6. **Log important events**: Help with debugging and auditing 7. **Handle empty cases**: Check for `= empty` before using objects 8. **Use WITHOUT EVENTS appropriately**: Only when handlers must be skipped (events are on by default) 9. **Validate before executing**: Use `mxcli check script.mdl -p app.mpr --references` to catch errors ## Related Documentation - [MDL Syntax Guide](../../docs/02-features/mdl-syntax.md) - [OQL Syntax Guide](../../docs/syntax-proposals/OQL_SYNTAX_GUIDE.md) - [Microflow Examples](../../examples/doctype-tests/microflow-examples.mdl) - [Mendix Microflow Documentation](https://docs.mendix.com/refguide/microflows/) ## Quick Reference ### Variable Declaration Pattern ```mdl declare $primitive type = value; -- Primitives (String/Integer/Decimal/Boolean/DateTime) declare $status Enumeration(Module.Enum) = …; -- Enumerations are primitives too -- Objects: never declare. Use a parameter, retrieve (… first), `$obj = create Module.Entity(...)`, or a loop iterator. -- Lists: never declare. Use a parameter, retrieve, or `$list = create list of Module.Entity;` ``` ### Object Operation Pattern ```mdl $var = create Module.Entity (attr = value); change $var (attr = value); commit $var [without events] [refresh]; ``` ### Flow Control Pattern ```mdl if condition then ... [else ...] end if; loop $var in $list begin ... end loop; return $value; ``` ### XPath Pattern ```mdl $var/attributename -- Attribute $var/Module.AssociationName -- Association $var/Module.AssociationName/attribute -- Chained ``` ### Annotation Pattern ```mdl @position(200, 200) -- optional: omit it and mxcli lays the flow out; to re-arrange an existing flow run `mxcli layout flows` @caption 'Persist order' @color Green @annotation 'Note about the next activity' commit $Order; -- Annotations apply here ``` ### Annotations Are Notes, and a Note Can Be Shared A note is a node with edges in Mendix, not a property of the activity it documents. So `@annotation` is **repeatable** — one activity can carry several, each its own note — and one note can be attached to several activities: ```mdl @annotation(id: n1, text: 'both of these touch the same record') commit $Order; @annotation(id: n1) -- attaches THAT note, does not copy it commit $Invoice; ``` `id:` is scoped to the flow you are writing and is not stored in the model; it exists only so a second mention can point at the first. **Without it, two lines with identical text are two separate notes** — mxcli never merges on text. A note's own canvas geometry is `position: (x, y)` and `size: (w, h)`, e.g. `@annotation(text: 'note', position: (175, -40), size: (260, 70))`. Omit them and the note goes 100px above the activity at 200×50, stacking 60px per extra note; DESCRIBE omits them again whenever they match, so an ordinary note keeps the short `@annotation 'text'` form. ### Page Navigation Pattern ```mdl show page Module.Page(Param = $value); close page; show home page; ``` ### Error Handling Pattern ```mdl call microflow ... on error continue; -- Ignore error call microflow ... on error rollback; -- Rollback on error call microflow ... on error begin log ...; return ...; end error; -- Custom handler call microflow ... on error without rollback begin ... end error; -- No rollback ``` The clause goes on whichever activity may fail, not only on calls: ```mdl declare $Name String = 'default' on error begin return 'could not initialise'; end error; set $Name = $Other/Name on error begin return 'lookup failed'; end error; change $Order (Status = Shipped) on error begin log error 'could not ship'; return; end error; log info node 'App' 'starting' on error begin return; end error; show message 'saved' on error begin return; end error; -- BLOCKING halts the client until dismissed; after `objects`, before `on error`. show message 'Hello {1}' type Warning with ({1} = $Name) blocking; validation feedback $Order/Total message 'must be positive' on error begin return; end error; show page Module.Page on error begin return; end error; close page on error begin return; end error; ``` **Two limits, both reported rather than silently ignored:** - **`on error continue` is rejected by Mendix** (CE6035) on `create`, `change`, `commit`, `log`, `show page`, `close page`, `show message` and `validation feedback` — **MDL076**. A custom `{ handler }` is accepted on all of them; `continue` is fine on `declare`, `set`, `retrieve`, `delete` and `call microflow`. Measured on 11.14.0 — note that create-*variable* and change-*variable* accept `continue` while change-*object* does not. - **List operations and aggregates** (`$x = head $l;`, `$n = count $l;`) have no error handling in Mendix at all — **MDL077**. **In a nanoflow, almost none of them take a clause at all.** `change`, `log`, `show page`, `close page`, `show message` and `validation feedback` are CE6035 there whichever form is written; only `declare` and `set` accept one. See `write-nanoflows`. **End the handler.** A handler body that does not finish with `return` or `throw` merges back into the main flow, so a variable created *after* the merge point is out of scope on the error path — CE0108, which Studio Pro reports for the same model. Ending the handler (as Studio Pro does when you wire it to an end event) avoids this entirely.