Summary
Any microflow that uses raise error; anywhere in its main flow — including as the sole
statement of an otherwise-empty microflow, or as one arm of an if/else where both branches are
individually terminal — fails native mxbuild with:
CE0710: "The main flow cannot join an error flow or end in an error event"
This is not a linter/mxcli check false-negative; it is a real, structurally invalid generated
model. raise error; used inside an on error { ... } handler block builds cleanly — the defect
is specific to raise error; appearing directly in the main flow.
Environment
- mxcli:
v0.20.0 (tag, built from source, git describe --tags = v0.20.0, clean tree)
- Mendix Studio Pro / mxbuild:
11.13.0
- OS: Linux amd64
- Baseline: a blank Mendix 11.13.0 project scaffolded fresh by
mxcli new
- Verdict source: native
mxbuild/mx check (mxcli docker check), confirmed with a direct
mxbuild --write-errors=... --target=deploy invocation bypassing mxcli's own check/lint
entirely
- Originally found on an earlier pinned mxcli build (same era as the BUG-70–BUG-77 cluster, prior
to v0.18.0); reproduced independently since on v0.20.0 (2026-08-31)
Steps to reproduce
Any of the following microflow bodies produce the identical CE0710, isolated by bisection
(least-to-most minimal):
-- 1: entire body is raise error
create microflow Test.MF_RaiseOnly ()
begin
raise error;
end;
/
-- 2: same, with a declared return type
create microflow Test.MF_RaiseTyped () returns boolean as $Result
begin
raise error;
end;
/
-- 3: both branches individually terminal
create microflow Test.MF_RaiseIf ($X: boolean) returns boolean as $Result
begin
if $X then
return true;
else
raise error;
end if;
end;
/
-- 4: guard-clause shape
create microflow Test.MF_RaiseGuard ($X: boolean) returns boolean as $Result
begin
if $X = false then
raise error;
end if;
return true;
end;
/
All four: mxcli check passes (or is only a warning under SKIP_CHECK=1, an unrelated
pre-existing MDL003 linter gap), mxcli exec succeeds, and native mx check/mxbuild reports:
[error] [CE0710] "The main flow cannot join an error flow or end in an error event"
Nesting depth, if/elsif/else flattening, and the presence/absence of a trailing return after the
raise error; statement all make no difference.
Expected vs. actual
Expected: raise error; as the final statement of a terminal branch (main flow or any
statement-processing scope) is treated as closing that scope, the same way a return statement
is — no trailing EndEvent/SequenceFlow should be appended after it.
Actual: mxcli's flow-graph builder unconditionally appends a trailing EndEvent (and wires an
outgoing SequenceFlow to it from whatever the last-processed node was) unless an internal
"ends with return" flag is set. That flag is set only by return-statement handling paths (and a
narrow if-both-branches-terminal special case that itself does not cover a branch ending in
raise error;) — never by raise error;'s own dispatch. So whenever a raise error;-created
ErrorEvent ends up being the last node in any scope, the closing logic wrongly appends a second
EndEvent and wires an illegal outgoing SequenceFlow FROM the terminal ErrorEvent — an
ErrorEvent cannot legally have an outgoing flow in real Mendix semantics, which is exactly
CE0710.
Severity
High. No MDL-only workaround exists — this blocks the plain, idiomatic way to propagate a
technical failure to a microflow's caller (raise error;) in the main flow entirely, for every
shape tried.
Workaround
Avoid raise error; in the main flow. Add a small Java action that always throws
(create java action Module.JA_RaiseTechnicalError(Message: string not null) returns boolean as $$ throw new com.mendix.systemwideinterfaces.MendixRuntimeException(Message); $$;) and call it as an
ordinary activity where raise error; was wanted, followed by a normal (unreachable at runtime,
but required to satisfy Mendix's static "every path returns" check) return statement. Calling a
Java action goes through regular activity codegen, unaffected by this bug, and the thrown
exception terminates the microflow with a runtime error visible to the caller's ON ERROR
handling the same way a native raise error activity would.
Suggested fix
The RaiseErrorStmt dispatch in the flow-graph builder should set the same "ends with return"
flag that ReturnStmt handling sets, and/or the per-branch statement loops inside if-statement
handling need to treat a branch ending in RaiseErrorStmt as already-closed the same way they
treat one ending in ReturnStmt.
Summary
Any microflow that uses
raise error;anywhere in its main flow — including as the solestatement of an otherwise-empty microflow, or as one arm of an
if/elsewhere both branches areindividually terminal — fails native mxbuild with:
This is not a linter/
mxcli checkfalse-negative; it is a real, structurally invalid generatedmodel.
raise error;used inside anon error { ... }handler block builds cleanly — the defectis specific to
raise error;appearing directly in the main flow.Environment
v0.20.0(tag, built from source,git describe --tags=v0.20.0, clean tree)11.13.0mxcli newmxbuild/mx check(mxcli docker check), confirmed with a directmxbuild --write-errors=... --target=deployinvocation bypassing mxcli's owncheck/lintentirely
to v0.18.0); reproduced independently since on v0.20.0 (2026-08-31)
Steps to reproduce
Any of the following microflow bodies produce the identical CE0710, isolated by bisection
(least-to-most minimal):
All four:
mxcli checkpasses (or is only a warning underSKIP_CHECK=1, an unrelatedpre-existing MDL003 linter gap),
mxcli execsucceeds, and nativemx check/mxbuildreports:Nesting depth, if/elsif/else flattening, and the presence/absence of a trailing
returnafter theraise error;statement all make no difference.Expected vs. actual
Expected:
raise error;as the final statement of a terminal branch (main flow or anystatement-processing scope) is treated as closing that scope, the same way a
returnstatementis — no trailing
EndEvent/SequenceFlowshould be appended after it.Actual: mxcli's flow-graph builder unconditionally appends a trailing
EndEvent(and wires anoutgoing
SequenceFlowto it from whatever the last-processed node was) unless an internal"ends with return" flag is set. That flag is set only by
return-statement handling paths (and anarrow
if-both-branches-terminal special case that itself does not cover a branch ending inraise error;) — never byraise error;'s own dispatch. So whenever araise error;-createdErrorEventends up being the last node in any scope, the closing logic wrongly appends a secondEndEventand wires an illegal outgoingSequenceFlowFROM the terminalErrorEvent— anErrorEventcannot legally have an outgoing flow in real Mendix semantics, which is exactlyCE0710.
Severity
High. No MDL-only workaround exists — this blocks the plain, idiomatic way to propagate a
technical failure to a microflow's caller (
raise error;) in the main flow entirely, for everyshape tried.
Workaround
Avoid
raise error;in the main flow. Add a small Java action that always throws(
create java action Module.JA_RaiseTechnicalError(Message: string not null) returns boolean as $$ throw new com.mendix.systemwideinterfaces.MendixRuntimeException(Message); $$;) and call it as anordinary activity where
raise error;was wanted, followed by a normal (unreachable at runtime,but required to satisfy Mendix's static "every path returns" check)
returnstatement. Calling aJava action goes through regular activity codegen, unaffected by this bug, and the thrown
exception terminates the microflow with a runtime error visible to the caller's
ON ERRORhandling the same way a native
raise erroractivity would.Suggested fix
The
RaiseErrorStmtdispatch in the flow-graph builder should set the same "ends with return"flag that
ReturnStmthandling sets, and/or the per-branch statement loops insideif-statementhandling need to treat a branch ending in
RaiseErrorStmtas already-closed the same way theytreat one ending in
ReturnStmt.