Skip to content

fix(step): don't crash when a single foreach reference resolves falsy - #6726

Closed
Prabal864 wants to merge 1 commit into
keephq:mainfrom
Prabal864:fix-6721-foreach-falsy-value
Closed

fix(step): don't crash when a single foreach reference resolves falsy#6726
Prabal864 wants to merge 1 commit into
keephq:mainfrom
Prabal864:fix-6721-foreach-falsy-value

Conversation

@Prabal864

Copy link
Copy Markdown
Contributor

What's changed?

Step._get_foreach_items used the classic X and Y or Z idiom to decide whether to return a single resolved foreach value directly, or zip() multiple &&-combined ones together:

return len(foreach_items) == 1 and foreach_items[0] or zip(*foreach_items)

That idiom is only safe when foreach_items[0] is never falsy. If a foreach: "{{ ... }}" reference legitimately resolves to a falsy scalar (0, False, ""), the expression falls through to zip(*foreach_items) even though there's only one item — and zip() requires an iterable argument, so it crashes with TypeError instead of returning the resolved value.

Fixed by replacing the idiom with an explicit conditional that branches on len(foreach_items) == 1 directly, rather than on the truthiness of the resolved value:

return foreach_items[0] if len(foreach_items) == 1 else zip(*foreach_items)

No behavior changes for the happy path (truthy single value, or multiple &&-combined references) — only the previously-crashing falsy-single-value case is fixed.

Closes #6721

How was this patch tested?

Added tests/test_step_foreach_falsy_value.py, which constructs a real Step (with a real ContextManager) and calls the actual _get_foreach_items, covering:

  • a single foreach reference resolving to 0, False, "", or 0.0 now returns that value instead of raising TypeError
  • a single truthy value is still returned unchanged
  • multiple &&-combined references still zip() together correctly

I wasn't able to run this test file (or the full suite) in my sandbox — same as the previous PR (#6720): poetry install fails on this platform because uvloop (a pyproject dependency) doesn't support Windows, so the project's actual dependencies never fully install. What I did verify:

  • python -m py_compile passes on both changed files.
  • black --check and isort --check pass on both changed files.
  • Extracted the exact before/after control flow (_get_foreach_items's return statement, verbatim) into a standalone script and ran it directly against 0, False, "", a truthy list, multiple &&-combined references, and the empty case: the old code raises TypeError on 0/False (and silently returns [] instead of "" for the empty-string case, via zip(*[""]) iterating zero characters) while the new code returns the correct value in every case, with no change for the already-working cases.

A maintainer running this on Linux/macOS (where uvloop installs fine) should see tests/test_step_foreach_falsy_value.py pass outright.

Checklist

  • I have read the Contributing Guide
  • If you've added code that should be tested, add tests.
  • If you've changed APIs, update the documentation. — not applicable, no API change.
  • Ensure the test suite passes.
  • Make sure your code lints (black/isort).

Step._get_foreach_items used the `X and Y or Z` idiom to pick between
returning a single resolved foreach value and zip()-ing multiple
&&-combined ones together. That idiom is only safe when Y is never
falsy: a foreach reference that legitimately resolves to 0, False, or
"" fell through to zip(*foreach_items) despite there being only one
item, and crashed with TypeError since scalars aren't iterable.

Replaced it with an explicit conditional that branches on
len(foreach_items) == 1 directly instead of on the truthiness of the
resolved value.

Closes keephq#6721
@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. Bug Something isn't working labels Aug 22, 2026
@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 30.46%. Comparing base (ce302f6) to head (dedf369).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
keep/step/step.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #6726       +/-   ##
===========================================
- Coverage   46.42%   30.46%   -15.96%     
===========================================
  Files         178      101       -77     
  Lines       18694    11773     -6921     
===========================================
- Hits         8679     3587     -5092     
+ Misses      10015     8186     -1829     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Prabal864

Copy link
Copy Markdown
Contributor Author

Closing this in favor of finding and fixing a fresh, not-yet-reported issue instead of one that was already filed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🐛 Bug]: Step._get_foreach_items crashes with TypeError when a single foreach reference resolves to 0 or False

1 participant