fix(workflows): reject non-list input 'enum' instead of crashing#3601
Merged
mnriem merged 1 commit intoJul 21, 2026
Merged
Conversation
A workflow input whose `enum` is a scalar or string (e.g. `enum: 5`,
`enum: "abc"`) previously slipped past `validate_workflow` and crashed
at run time. The `value not in enum_values` membership test in
`_coerce_input` raises a raw `TypeError` ("argument of type 'int' is
not iterable") for a scalar, and a bare string turns enum membership
into a silent substring test. The `TypeError` also escapes
`validate_workflow`'s `except ValueError`, breaking its documented
"return a list of errors, never raise" contract.
This is the same unvalidated-`execute()` crash class as the fan-in
`wait_for` (github#3482) and fan-out step-template (github#3537) fixes: `validate()`
should reject the value, but the value can still reach the engine via
`execute()`, which accepts unvalidated definitions.
Fix:
- `_coerce_input` requires a list `enum` (or `None`), raising a clean
ValueError for any other shape — so both `validate_workflow` and
runtime `_resolve_inputs` fail fast with a clear message.
- `validate_workflow` checks `enum` shape directly (not only via the
default-coercion path, which is reached only when a `default` exists),
and strips a malformed `enum` before coercing the default so the
wrong-typed-default error is not duplicated as an enum-shape error.
- The `integration: auto` sentinel only strips a *list* `enum`; a
non-list `enum` stays in the definition so it is rejected rather than
silently exempted by the `auto` membership skip.
Tests cover all three layers: `_coerce_input` directly, authoring-time
`validate_workflow` (with no default present), and runtime
`_resolve_inputs`, plus the `integration: auto` interaction.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Rejects malformed workflow input enums during validation and runtime resolution, preventing raw TypeError crashes and substring matching.
Changes:
- Enforces list-or-
Noneenum values. - Preserves
integration: autohandling without exempting malformed enums. - Adds validation, coercion, and runtime regression tests.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/workflows/engine.py |
Validates enum shape and safely handles default coercion. |
tests/test_workflows.py |
Covers malformed enums across validation and runtime paths. |
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: Medium
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.
Problem
A workflow input whose
enumis not a list slips past validation and crashes at run time:_resolve_inputs→_coerce_inputrunsvalue not in enum_values, andx not in 5raises a rawTypeError: argument of type 'int' is not iterable.TypeErrorescapesvalidate_workflow'sexcept ValueError, breaking its documented "return a list of errors, never raise" contract — so authoring-time validation crashes instead of reporting an error.enum: "abc"doesn't crash but turns enum membership into a substring test (value in "abc"), silently accepting"a","b","ab", etc.This is the same unvalidated-
execute()crash class as #3482 (fan-inwait_for) and #3537 (fan-out step template):validate()should reject the value, butexecute()accepts unvalidated definitions, so the bad value still reaches the engine.Fix
_coerce_inputnow requiresenumto be alist(orNone), raising a cleanValueErrorwith a clear message for any other shape. Both the validation path and runtime resolution now fail fast.validate_workflowchecks theenumshape directly, not only via the default-coercion path — that path only runs when adefaultis present, so a defaultless badenumwould otherwise slip through. A malformedenumis stripped before coercing the default so the wrong-typed-default error isn't duplicated as an enum-shape error.integration: autosentinel only strips a listenumbefore coercion (enum membership is a runtime concern forauto). A non-listenumstays in the definition so it's rejected rather than silently exempted.Behavior
Before →
TypeErrorescapes / silent substring match.After →
Input 'scope' has invalid 'enum': must be a list, got int.at both authoring and run time.Tests
New coverage across all three layers:
_coerce_inputdirectly (scalar, bool, string, dict; valid list andNonestill work)validate_workflowwith no default present (the path the default-coercion check misses)_resolve_inputsintegration: autointeraction (non-listenumstill rejected)Full
tests/test_workflows.pypasses locally except the pre-existing Windows symlink-guard tests, which require elevation and are unrelated to this change.🤖 Generated with Claude Code