Skip to content

fix(workflows): fail fan-in step on non-list wait_for instead of crashing#3482

Open
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/fanin-nonlist-waitfor-crash
Open

fix(workflows): fail fan-in step on non-list wait_for instead of crashing#3482
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/fanin-nonlist-waitfor-crash

Conversation

@Noor-ul-ain001

Copy link
Copy Markdown
Contributor

Summary

FanInStep.validate() and the engine's fan-in wiring checks both reject a non-list wait_for, but the engine's execute() path does not auto-validate — WorkflowEngine.load_workflow's docstring notes the returned definition is "not yet validated; call validate_workflow() or engine.validate() separately."

On such an unvalidated run, FanInStep.execute iterated the raw value (for step_id in wait_for), with two bad outcomes depending on the type:

  • Scalar (wait_for: 5, wait_for: null) → TypeError, which took down the whole run, since the engine calls step_impl.execute() with no surrounding try/except.
  • String (wait_for: stepA) → silently iterated the string's characters, returning a join of empty results with a COMPLETED status:
>>> FanInStep().execute({"id": "f", "wait_for": "stepA"}, ctx).output["results"]
[{}, {}, {}, {}, {}]   # one bogus empty result per character, COMPLETED

That silent-wrong case is exactly the "silent empty result + COMPLETED" wiring bug the engine's own fan-in validation comment (engine.py) warns against.

Fix

Guard execute to return a FAILED StepResult naming the type error — mirroring the fan-out step's non-list items handling (test_execute_non_list_items_fails_loudly) and the switch/max_iterations/timeout/fan-in-output guards. A missing wait_for key still defaults to an empty list (COMPLETED), unchanged; the guard fires only on an explicit non-list value.

Same bug class as #3481 and prior fixes: a validator catches the bad value, but the runtime path crashes (or silently misbehaves) on it when validation is skipped.

Testing

  • Added TestFanInStep::test_execute_non_list_wait_for_fails_loudly (parametrized over str, int, None, dict).
  • Full tests/test_workflows.py passes except the 11 pre-existing Windows symlink-guard tests (fail identically on a clean base; require elevation).
  • ruff check clean on the changed files.

🤖 Generated with Claude Code

…hing

`FanInStep.validate()` and the engine's fan-in checks both reject a
non-list `wait_for`, but the engine's `execute()` path does not
auto-validate (see `WorkflowEngine.load_workflow`, whose docstring notes
the definition is "not yet validated"). On an unvalidated run, `execute`
iterated the raw value with `for step_id in wait_for`, with two bad
outcomes:

  * a scalar (`wait_for: 5`, `wait_for: null`) raised `TypeError` and
    took down the whole run — the engine invokes `step_impl.execute()`
    with no surrounding try/except; and
  * a string (`wait_for: stepA`) silently iterated its characters and
    returned a join of empty results with a COMPLETED status — the exact
    "silent empty result + COMPLETED" wiring bug the engine's own fan-in
    validation comment warns against.

Guard `execute` to return a FAILED StepResult naming the type error
instead, mirroring the fan-out step's non-list `items` handling. A
missing `wait_for` key still defaults to an empty list (COMPLETED),
unchanged; the guard fires only on an explicit non-list value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Noor-ul-ain001 Noor-ul-ain001 requested a review from mnriem as a code owner July 12, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant