Repository navigation
Conversation
… failing activity (mendixlabs#1565) A custom error handler that falls through, on an activity inside an IF, CASE or split type branch, was wired outside the branch. With a result variable it rejoined on the flow into the split, so a failing call re-ran the decision and called again; without one it ended the microflow at an end event of its own. In a branch ending in return it rejoined after the IF. mx check accepts all three. The builder keeps a handler pending until the next flow is drawn from the failing activity. buildFlowGraph settles it on every flow it draws; the branch loops settled only an empty handler. Each branch now settles its handlers on its own flows and on its exit flow to the merge. The handlers pending when a split starts belong to the activity before it, so each branch sets them aside and restores them afterwards; otherwise a result-variable handler, which settles on any flow, would rejoin in the middle of the first branch. In a branch that ends in return, a handler on an activity with a result still goes to the code after the split, the shared error path of a success tail built on the result (TestBuildFlowGraph_OutputHandlerInReturningBranchSkipsDerivedSuccessTail). Such a handler, and any other a branch cannot settle, is pinned to the split's exit, so it rejoins on the flow leaving the split rather than the next flow the parent draws, the one into it. Behaviour change: a handler on an activity WITHOUT a result in a branch that returns now runs the rest of its branch rather than the code after the IF, so the IF no longer needs a merge for it (thenNeedsErrorMerge and addPendingErrorHandlerFlowTo are removed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Closing: fixed on main by 27e533f, which covers the same shapes and more. The ten branch cases and the before-the-IF guard from this PR's test all pass on main. Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1565.
What it fixes
A custom error handler that doesn't end in
returnorraise errorcontinues with the statement after the failing activity. On an activity inside an IF, CASE orsplit typebranch, it was wired outside the branch:$N = call microflow …,$Response = call rest service … returns response)returnmx checkaccepts all of these except the CE0109 case.Cause
The builder keeps a handler pending until the next flow is drawn from the failing activity.
buildFlowGraphsettles it on every flow it draws. The branch loops settled only an empty handler (if fb.emptyErrorHandlerFrom == lastID); the IF-without-ELSE andsplit typeloops didn't settle any. So a handler with a body was still pending when the branch ended.The parent draws the flow into the split only after it has built the split. A handler with a result variable settles on the next flow, whatever that flow's origin, and that flow was the one into the split. A handler without one matched nothing, so the end-of-flow pass gave it an end event.
How
split typesettle on every flow they draw, asbuildFlowGraphdoes. They also settle on the branch's exit flow to the merge, which covers a handler on the branch's last activity.enterBranchErrorHandlers/takeBranchErrorHandlers).return, a handler on an activity with a result still goes to the code after the split, the shared error path of a success tail built on the result (TestBuildFlowGraph_OutputHandlerInReturningBranchSkipsDerivedSuccessTail, unchanged and passing).queueBranchErrorHandlersAt).split typedraws its exit flows only after all its branches are built, so each branch keeps its leftover handlers with its tail.return, now runs the rest of its branch,returnincluded, rather than the code after the IF. That's the documented rule and the third shape in the issue. The IF no longer needs a merge for it, sothenNeedsErrorMerge/elseNeedsErrorMergeandaddPendingErrorHandlerFlowTo(no callers left) are removed. If you'd rather keep the old routing for this case too, it's a one-line condition inaddBranchErrorHandlerFlowForStatement.Unchanged:
pendingElseErrorOrigin);returnorraise error;joinin the handler: the workaround shape from the comment above is stored identically (objects, positions, flows) bymainand by this branch.Also included: CHANGELOG (Fixed), a finding in
findings/mdl-executor/, andmdl-examples/bug-tests/1565-branch-error-handler-rejoin.mdl.Evidence
mdl/executor/cmd_microflows_builder_branch_handler_test.go, built from parsed MDL.split type.mainand pass here.mx checkafterexec:describe.mainand here. Only its merge position moves, from where the IF's merge was to the edge it splits.mx checkreports no new error. The CE0109 thatmainproduced for the named-return case is gone.execreports all documents unchanged.go test ./mdl/executor/...fails the same 28 tests as unchangedmain, and no others.go vetandgofmtare clean.golang:1.26.6-bookworm, ANTLR 4.13.2):grammar,build,test,check-mdl,check-findings,check-conformance,check-migration-referenceandlint-goall pass.🤖 Generated with Claude Code