Skip to content

fix(workflows): reject non-string 'condition' in if/while/do-while steps#3706

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/conditional-steps-condition-type-guard
Open

fix(workflows): reject non-string 'condition' in if/while/do-while steps#3706
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/conditional-steps-condition-type-guard

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Problem

IfThenStep, WhileStep, and DoWhileStep validate() confirm that condition is present but never that it is a string.

At run time execute() feeds condition to evaluate_condition():

def evaluate_expression(template, context):
    if not isinstance(template, str):
        return template   # non-string returned as-is
    ...

def evaluate_condition(condition, context):
    result = evaluate_expression(condition, context)
    ...
    return bool(result)

So a non-string condition is returned unchanged and bool()-coerced. A list/dict/number authoring mistake silently resolves to a truthiness instead of erroring:

  • condition: [1, 2]bool([1, 2])always True → if-branch always taken / while-loop spins to max_iterations.
  • condition: {} → always False, etc.

No error is reported, so the mistake is invisible.

Fix

Reject a present-but-non-string condition in each of the three steps' validate(), mirroring the existing prompt/shell/command 'must be a string' guards. "true"/"false" and expressions like "{{ inputs.flag }}" are strings, so they remain valid — no behaviour change for correct workflows.

Verification

  • New parametrized tests ([list, dict, int, bool]) across all three step classes: fail before the guard (12 failures — the non-string conditions pass validation), pass after.
  • test_validate_accepts_string_condition confirms "true"/"false"/"{{ ... }}" still validate.
  • Full TestIfThenStep/TestWhileStep/TestDoWhileStep: 53 passed.
  • ruff check clean on all changed files.

AI-assisted: authored with Claude Code. I traced the runtime path (evaluate_conditionbool()), confirmed the silent-coercion on main, and verified the guards fail-before/pass-after.

`if_then`, `while_loop`, and `do_while` validate() confirm `condition` is
present but never that it is a string. execute() feeds it to
`evaluate_condition()`, which returns a non-string as-is and takes `bool()`
of it -- so `condition: [1, 2]` (a list authoring mistake) silently resolves
to `True`, branching wrongly / spinning the loop to `max_iterations`, with no
error reported.

Reject a present-but-non-string `condition` at validation, mirroring the
existing prompt/shell/command 'must be a string' guards. `"true"`/`"false"`
and expressions like `"{{ ... }}"` are strings, so they stay valid.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jawwad-ali
jawwad-ali requested a review from mnriem as a code owner July 24, 2026 08:23
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