diff --git a/lib/cli/src/crewai_cli/run_declarative_flow.py b/lib/cli/src/crewai_cli/run_declarative_flow.py index d3c58c1cf..7fd727698 100644 --- a/lib/cli/src/crewai_cli/run_declarative_flow.py +++ b/lib/cli/src/crewai_cli/run_declarative_flow.py @@ -80,6 +80,13 @@ def _resolve_flow_inputs(flow: Any, provided: dict[str, Any]) -> dict[str, Any]: # dict / unschematized state — nothing to derive; pass inputs through. return dict(provided) + # ``id`` signals a persistence restore: kickoff hydrates the full state from + # storage, so required fields may come from the restored state rather than + # --inputs. Forward the inputs unchanged instead of prompting/erroring for + # fields the resume will supply. + if "id" in provided: + return dict(provided) + properties = { name: spec for name, spec in (schema.get("properties") or {}).items() @@ -89,11 +96,10 @@ def _resolve_flow_inputs(flow: Any, provided: dict[str, Any]) -> dict[str, Any]: defaults = _flow_state_defaults(flow) # Unknown keys are almost always typos — warn and drop them (they'd fail - # structured-state validation at kickoff anyway). ``id`` is a reserved - # kickoff key (persistence restore), so let it through untouched. + # structured-state validation at kickoff anyway). collected: dict[str, Any] = {} for key, value in provided.items(): - if key in properties or key == "id": + if key in properties: collected[key] = value continue suggestion = _closest_key(key, properties) diff --git a/lib/cli/tests/test_run_declarative_flow.py b/lib/cli/tests/test_run_declarative_flow.py index 3364135a1..b45f40e59 100644 --- a/lib/cli/tests/test_run_declarative_flow.py +++ b/lib/cli/tests/test_run_declarative_flow.py @@ -351,3 +351,14 @@ def test_reserved_id_input_is_forwarded_not_dropped( captured = capsys.readouterr() assert captured.out == "a@b.com\n" assert "Ignoring unknown input 'id'" not in captured.err + + +def test_id_only_input_skips_required_validation(tmp_path: Path) -> None: + # Resume via `crewai run --inputs '{"id":"..."}'` must not be blocked by the + # required-field check: kickoff hydrates required state from persistence. + path = _write(tmp_path, REQUIRED_FLOW_YAML) + flow = run_declarative_flow_module.load_declarative_flow(str(path)) + + resolved = run_declarative_flow_module._resolve_flow_inputs(flow, {"id": "run-123"}) + + assert resolved == {"id": "run-123"}