aglinxinyuan opened a new issue, #7155:
URL: https://github.com/apache/texera/issues/7155
### What happened?
The GUI lets you connect **two upstream operators into a Loop Start's single
input port**. Nothing in the editor flags it — no link is refused, validation
stays green — and the workflow only fails when you run it:
```
java.lang.IllegalArgumentException: requirement failed:
Loop Start input port GlobalPortIdentity(
PhysicalOpIdentity(OperatorIdentity(LoopStart-operator-...),main),
PortIdentity(0,false), true)
expected exactly one reader URI, got 2
at
WorkflowExecutionManager.loopStartStateUris(WorkflowExecutionManager.scala:69)
at StartWorkflowHandler.startWorkflow(StartWorkflowHandler.scala:45)
```
The single-input restriction is **intended** — fan-in belongs in a `Union`
before the loop, as discussed in #6966. The bug is that the restriction is
invisible until run time.
### How to reproduce?
1. Drop a Loop Start and two source operators on the canvas.
2. Connect both sources to the Loop Start's input port — both links are
accepted.
3. Run the workflow → it fails at `StartWorkflow` with the error above.
### Root cause
`InputPort` already carries a `disallowMultiLinks` flag, and the frontend
honors it in two independent places:
| Guard | Where | Reads the flag from |
|---|---|---|
| Editor refuses to draw a second link into the port |
`workflow-editor.component.ts` | operator metadata
(`additionalMetadata.inputPorts[i].disallowMultiLinks`) |
| Validation requires exactly one input on the port |
`validation-workflow.service.ts` | the operator predicate, via
`WorkflowUtilService.inputPortToPortDescription` mapping `disallowMultiLinks` →
`disallowMultiInputs` |
The loop operators never set it — `LoopOpDesc.operatorInfo` declares a plain
`InputPort()`.
### Expected behavior
| | before | after |
|---|---|---|
| Drawing a 2nd link into a loop operator's input port | allowed | refused
by the editor |
| Workflow with 2 links into a loop operator | validates clean, fails at
`StartWorkflow` | flagged invalid in the editor |
| Programmatically built plan (no GUI) | fails at `StartWorkflow` |
unchanged — the scheduler `require` stays as a backstop |
Setting the flag on the shared `LoopOpDesc` input port covers both loop
operators, which is correct for Loop End too: every reader on a materialized
input port replays that port's states independently, so a second link would
deliver the loop state twice per iteration (double `update`, double back-edge).
Note: a saved workflow that already has two links into a loop operator will
start showing as invalid in the editor rather than failing at run time —
intended, since such a plan cannot execute today either.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]