Skip to content

Commit 8554167

Browse files
hjothaclaude
authored andcommitted
fix: treat terminal nested IF as returning in flow builder
lastStmtIsReturn only matched a literal ast.ReturnStmt as the last statement, missing the case where a body ends in an IF whose branches both return. The outer IF then thought its THEN branch continued, emitted a duplicate "true" SequenceFlow plus an orphan EndEvent with no ReturnValue, and Studio Pro rejected the file with "Sequence contains no matching element" on any diff or microflow open. Widen the predicate to "last stmt is a guaranteed terminator" — RETURN, RAISE ERROR, or an IF/ELSE where every branch is terminal (recursively). LOOP is explicitly not terminal because BREAK exits even if the body returns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 96a5d9e commit 8554167

1 file changed

Lines changed: 29 additions & 3 deletions

File tree

mdl/executor/cmd_microflows_builder_flows.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,37 @@ func newUpwardFlow(originID, destinationID model.ID) *microflows.SequenceFlow {
172172
}
173173
}
174174

175-
// lastStmtIsReturn checks if the last statement in a body is a RETURN statement.
175+
// lastStmtIsReturn reports whether execution of a body is guaranteed to terminate
176+
// (via RETURN or RAISE ERROR) on every path — i.e. control can never fall off the
177+
// end of the body into the parent flow.
178+
//
179+
// Terminal statements: ReturnStmt, RaiseErrorStmt. An IfStmt is terminal iff it
180+
// has an ELSE and both branches are terminal (recursively). A LoopStmt is never
181+
// terminal — BREAK can exit the loop even if the body returns.
182+
//
183+
// Naming kept for history; the predicate is really "last stmt is a guaranteed
184+
// terminator". Missing this case causes the outer IF to emit a dangling
185+
// continuation flow (duplicate "true" edge + orphan EndEvent), which Studio Pro
186+
// rejects as "Sequence contains no matching element" when diffing.
176187
func lastStmtIsReturn(stmts []ast.MicroflowStatement) bool {
177188
if len(stmts) == 0 {
178189
return false
179190
}
180-
_, ok := stmts[len(stmts)-1].(*ast.ReturnStmt)
181-
return ok
191+
return isTerminalStmt(stmts[len(stmts)-1])
192+
}
193+
194+
func isTerminalStmt(stmt ast.MicroflowStatement) bool {
195+
switch s := stmt.(type) {
196+
case *ast.ReturnStmt:
197+
return true
198+
case *ast.RaiseErrorStmt:
199+
return true
200+
case *ast.IfStmt:
201+
if len(s.ElseBody) == 0 {
202+
return false
203+
}
204+
return lastStmtIsReturn(s.ThenBody) && lastStmtIsReturn(s.ElseBody)
205+
default:
206+
return false
207+
}
182208
}

0 commit comments

Comments
 (0)