fix(workflows): gate validate() must not crash on non-string options#3233
Merged
mnriem merged 1 commit intoJun 29, 2026
Merged
Conversation
GateStep.validate() reports non-string options as an error, but then -- when on_reject is 'abort'/'retry' -- still runs the reject-choice check 'any(o.lower() in ... for o in options)'. For a non-string option (e.g. options: [123]) o.lower() raised AttributeError, which escaped validate() and broke validate_workflow's documented 'return a list of errors, never raise' contract. Guard the check so it only runs when every option is a string (the non-string case is already reported above). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes an uncaught AttributeError in GateStep.validate() when options contains non-string values and on_reject is abort/retry, ensuring workflow validation continues to follow the “return a list of error messages” contract instead of raising.
Changes:
- Added a guard so the reject-choice text check only runs when all
optionsentries are strings. - Added a regression test covering non-string
optionsfor defaulton_rejectand expliciton_reject: retry.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/workflows/steps/gate/__init__.py |
Prevents validate() from calling .lower() on non-string options by gating the reject-choice check. |
tests/test_workflows.py |
Adds a regression test to ensure non-string options are reported as validation errors rather than raising. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Low
Collaborator
|
Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
GateStep.validate()reports non-stringoptionsas an error, but then — whenon_rejectisabort/retry(abortis the default) — still runs the reject-choice check:For a non-string option (e.g.
options: [123]),o.lower()raisesAttributeError: 'int' object has no attribute 'lower', which escapesvalidate()and breaksvalidate_workflow's documented contract — "return a list of error messages", never raise. A single malformed gate option crashes validation of the whole workflow instead of being reported.Fix
Run the option-text check only when every option is a string (the non-string case is already reported by the earlier check). One guard clause.
Testing
uvx ruff checkclean;TestGateSteppasses (14 tests).test_validate_non_string_options_does_not_raise:options: [123](defaulton_reject) andoptions: [True], on_reject: retrynow return the "must be strings" error instead of raising. Fails before (AttributeError), passes after.GateStepon current main; valid string options still produce the correct reject-choice error.AI Disclosure
Found and fixed with Claude Code (Claude Opus 4.8) under my direction. AI traced the uncaught
AttributeErrorfromo.lower()throughvalidate_workflow's never-raise contract; I reproduced the crash against the realGateStep, confirmed string options still validate correctly, and reviewed the diff before submitting.