Skip to content

Commit d6e09a1

Browse files
fix(workflows): validate non-string step types (#4111)
Return an actionable validation error when a workflow step type is a YAML list or mapping instead of raising during registry membership checks. Assisted-by: OpenAI Codex (model: GPT-5, autonomous)
1 parent 672f812 commit d6e09a1

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

src/specify_cli/workflows/engine.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,17 @@ def _validate_steps(
366366

367367
# Determine step type
368368
step_type = step_config.get("type", "command")
369+
if not isinstance(step_type, str):
370+
# Registry keys are strings. Checking an unhashable YAML value
371+
# (for example ``type: [shell]`` or a mapping) against the set
372+
# below raises a raw TypeError before validation can report the
373+
# authoring mistake. Guard every non-string shape first, matching
374+
# the typed validation already applied to workflow and step IDs.
375+
errors.append(
376+
f"Step {step_id!r}: 'type' must be a string, got "
377+
f"{type(step_type).__name__} ({step_type!r})."
378+
)
379+
continue
369380
if step_type not in _get_valid_step_types():
370381
errors.append(
371382
f"Step {step_id!r} has invalid type {step_type!r}."

tests/test_workflows.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4565,6 +4565,29 @@ def test_invalid_step_type(self):
45654565
errors = validate_workflow(definition)
45664566
assert any("invalid type" in e.lower() for e in errors)
45674567

4568+
@pytest.mark.parametrize("step_type", [["shell"], {"name": "shell"}])
4569+
def test_non_string_step_type_reports_error(self, step_type):
4570+
"""Unhashable YAML values must not crash registry membership checks."""
4571+
from specify_cli.workflows.engine import WorkflowDefinition, validate_workflow
4572+
4573+
definition = WorkflowDefinition(
4574+
{
4575+
"workflow": {
4576+
"id": "test",
4577+
"name": "Test",
4578+
"version": "1.0.0",
4579+
},
4580+
"steps": [{"id": "bad", "type": step_type}],
4581+
}
4582+
)
4583+
4584+
errors = validate_workflow(definition)
4585+
4586+
assert errors == [
4587+
f"Step 'bad': 'type' must be a string, got "
4588+
f"{type(step_type).__name__} ({step_type!r})."
4589+
]
4590+
45684591
def test_nested_step_validation(self):
45694592
from specify_cli.workflows.engine import WorkflowDefinition, validate_workflow
45704593

0 commit comments

Comments
 (0)