Skip to content

Commit 4e1049e

Browse files
kingpanther13claude
andcommitted
fix(flows): point the empty-forms error at step_values step ids
step_values is a directive, not a field, so a config of nothing but a step_values entry naming a step the flow never presents tripped the empty-forms guard and was told to check its field names. The likely mistake there is the step_id. Guidance now branches on what was actually supplied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012d29UJTiH4Uy2Pm37SBPtr
1 parent 8651b03 commit 4e1049e

2 files changed

Lines changed: 69 additions & 6 deletions

File tree

src/ha_mcp/tools/config_entry_flow_walker.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from ..redaction import redact_flow_schema, redaction_enabled
2020
from .config_entry_flow_form import (
2121
_MENU_SELECTION_KEYS,
22+
_PER_STEP_VALUES_KEY,
2223
_auto_confirm_form_payload,
2324
_handle_form_step,
2425
_ReuseState,
@@ -493,6 +494,27 @@ def _raise_reconfigure_no_answer(
493494
)
494495

495496

497+
def _empty_forms_suggestions(supplied_keys: list[str]) -> list[str]:
498+
"""Guidance for a flow that consumed none of the caller's keys.
499+
500+
``step_values`` is a directive rather than a field, so "check the field
501+
names" is the wrong advice when it is what the caller supplied: the likely
502+
mistake is a step_id the flow never presents, not a misspelled field.
503+
"""
504+
if _PER_STEP_VALUES_KEY in supplied_keys:
505+
return [
506+
"Check each step_values step_id against the steps this flow "
507+
"actually presents — a step_id the flow never reaches applies "
508+
"nothing — and check the field names inside each entry with "
509+
"ha_get_integration(entry_id=..., include_schema=True).",
510+
]
511+
return [
512+
"Check the field names against the flow's data_schema — "
513+
"ha_get_integration(entry_id=..., include_schema=True) "
514+
"shows the accepted fields — then retry with corrected keys.",
515+
]
516+
517+
496518
def _finish_flow_entry(
497519
flow_id: str,
498520
current_step: dict[str, Any],
@@ -521,12 +543,7 @@ def _finish_flow_entry(
521543
"Flow completed without consuming any of the supplied "
522544
"config keys — every form step was submitted empty, so "
523545
"the flow saved its defaults, not your values",
524-
suggestions=[
525-
"Check the field names against the flow's data_schema — "
526-
"ha_get_integration(entry_id=..., include_schema=True) "
527-
"shows the accepted fields — then retry with corrected "
528-
"keys.",
529-
],
546+
suggestions=_empty_forms_suggestions(supplied_keys),
530547
context={
531548
"flow_id": flow_id,
532549
"supplied_keys": supplied_keys,

tests/src/unit/test_flow_options_preserve.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,52 @@ def test_a_schemaless_step_does_not_submit_the_reserved_key(self) -> None:
10411041
assert payload == {"host": "10.0.0.5"}
10421042

10431043

1044+
class TestEmptyFormsGuidance:
1045+
"""The empty-forms error must point at the right mistake."""
1046+
1047+
async def test_a_step_values_only_config_is_told_to_check_step_ids(self) -> None:
1048+
"""step_values is a directive, so "check the field names" misleads.
1049+
1050+
A config of nothing but step_values that names a step the flow never
1051+
presents consumes no field key and trips the empty-forms guard. The
1052+
likely mistake there is the step_id, not a misspelled field.
1053+
"""
1054+
submit_fn = AsyncMock(
1055+
side_effect=[{"type": "create_entry", "result": {"entry_id": "e"}}]
1056+
)
1057+
1058+
with pytest.raises(ToolError) as exc_info:
1059+
await _handle_flow_steps(
1060+
client=None,
1061+
flow_id="flow-2254",
1062+
initial_step=_workday_options_step(),
1063+
config={"step_values": {"never_shown": {"days_offset": 3}}},
1064+
submit_fn=submit_fn,
1065+
keep_current_values=True,
1066+
)
1067+
1068+
message = str(exc_info.value)
1069+
assert "step_values step_id" in message
1070+
assert "a step_id the flow never reaches" in message
1071+
1072+
async def test_a_plain_typo_still_gets_the_field_name_guidance(self) -> None:
1073+
submit_fn = AsyncMock(
1074+
side_effect=[{"type": "create_entry", "result": {"entry_id": "e"}}]
1075+
)
1076+
1077+
with pytest.raises(ToolError) as exc_info:
1078+
await _handle_flow_steps(
1079+
client=None,
1080+
flow_id="flow-2254",
1081+
initial_step=_workday_options_step(),
1082+
config={"days_ofset": 3},
1083+
submit_fn=submit_fn,
1084+
keep_current_values=True,
1085+
)
1086+
1087+
assert "Check the field names" in str(exc_info.value)
1088+
1089+
10441090
class TestBackfillIsNotCallerConsumption:
10451091
"""Schema data must not make a config of typos look partially applied."""
10461092

0 commit comments

Comments
 (0)