fix(workflows): fail switch step on non-mapping cases instead of crashing (#3481)

`SwitchStep.validate()` already rejects a non-mapping `cases`, 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` called
`cases.items()` on the raw value, so a list or scalar `cases` authoring
mistake raised `AttributeError` and took down the whole run — the engine
invokes `step_impl.execute()` with no surrounding try/except.

Guard `execute` to return a FAILED StepResult naming the type error
instead, mirroring the fan-out step's non-list `items` handling. The
expression is still evaluated first, so its value is surfaced in the
step output for downstream context.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Noor ul ain
2026-07-13 23:02:04 +05:00
committed by GitHub
parent e649bbdc44
commit e590cd8007
2 changed files with 43 additions and 0 deletions

View File

@@ -26,6 +26,20 @@ class SwitchStep(StepBase):
str_value = str(value) if value is not None else ""
cases = config.get("cases", {})
if not isinstance(cases, dict):
# The engine does not auto-validate step config, so an unvalidated
# run with a non-mapping ``cases`` (a list/scalar authoring mistake)
# would otherwise raise AttributeError from ``.items()`` below and
# crash the whole run. Fail this step loudly instead, mirroring the
# fan-out step's non-list ``items`` handling.
return StepResult(
status=StepStatus.FAILED,
error=(
f"Switch step {config.get('id', '?')!r}: 'cases' must be a "
f"mapping, got {type(cases).__name__}."
),
output={"matched_case": None, "expression_value": value},
)
for case_key, case_steps in cases.items():
if str(case_key) == str_value:
return StepResult(