[Detail Bug] KNX GUI: Invalid numeric input resets integer parameters to minimum and persists the corrupted value
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 4
- Forks
- 0
- Avg merge
- 15h 38m
- Merged PRs (30d)
- 37
Description
Detail Bug Report
Introduced in 09f490434fc1712370be798abbd5984272ff19cd by @kewde on Jun 6, 2026
Summary
- Context:
_render_int_paraminapps/knx-gui/src/knx_gui/widgets/parameter_widgets.pyrenders every integer parameter (bothNumberWidgetandNumberSliderWidget) as one-line text fields and clamps the typed value when editing finishes. - Bug: When the field's text can't be parsed as a plain integer, the
except ValueErrorbranch silently overwrites the parameter's bound value withmin_valueinstead of preserving the prior value — destroying the previously-valid value and writing it back throughon_change. - Actual vs. expected: A single non-integer character that
imgui.InputTextFlags_.chars_decimaladmits (., but also+,-mid-string,*,/) — e.g. typing5.5into a50valued parameter — silently resets the stored parameter tomin_value(often0) on blur; the expected behavior is to reject the unparseable input and keep the prior value. - Impact: Corruption of a saved project's parameter state. The wrong value is persisted through
ProjectService.set_parameter→EventStore.append(SetParameter(...))to a SQLite event history that survives app reopen (perEventStore's contract: "the history is fully re-playable and survives reopen") and is re-applied live (SetParameter.applysetsparam.valueon theParameterORM row).
Code with Bug
def _render_int_param(
widget_id: str,
value: str,
min_value: int | None,
max_value: int | None,
on_change: Callable[[str], None],
) -> None:
_, new_text = imgui.input_text(
f"##{widget_id}", value, imgui.InputTextFlags_.chars_decimal
)
if imgui.is_item_deactivated_after_edit():
try:
clamped = int(new_text)
except ValueError:
clamped = min_value if min_value is not None else 0 # <-- BUG 🔴 overwrites prior valid value with min_value on unparseable input
if min_value is not None:
clamped = max(min_value, clamped)
if max_value is not None:
clamped = min(max_value, clamped)
if str(clamped) != value:
on_change(str(clamped))
Explanation
imgui.InputTextFlags_.chars_decimalallows0123456789.+-*/, so users can type strings like5.5,1+2,3/4, etc._render_int_paramparses the buffer withint(new_text), which rejects those inputs and raisesValueError.- The
except ValueErrorhandler replaces the value withmin_value(commonly0) and then callson_change, so the UI change becomes a real model update. - That update is persisted via the Configure panel callback chain (
ProjectPlugin._handle_param_change→ProjectService.set_parameter→EventStore.append(SetParameter(...))), and replayed on reopen (SetParameter.applyassignsparam.value = self.value).
Codebase Inconsistency
- The fallback to
min_valuewas originally used when parsing a loaded default value feedingimgui.drag_int(which cannot produce non-integers). After commit09f4904, the control switched to free-text input (input_text+chars_decimal) but kept the same fallback, changing its meaning from recover from malformed loaded defaults to silently overwrite user-entered invalid text with minimum.
Recommended Fix
On ValueError, do not substitute min_value; reject the unparseable input and keep the prior bound value:
if imgui.is_item_deactivated_after_edit():
try:
clamped = int(new_text)
except ValueError:
return # <-- FIX 🟢 reject unparseable input, keep prior bound value
if min_value is not None:
clamped = max(min_value, clamped)
if max_value is not None:
clamped = min(max_value, clamped)
if str(clamped) != value:
on_change(str(clamped))
History
This bug was introduced in commit 09f4904. That refactor extracted integer/time parameter rendering into _render_int_param and switched from imgui.drag_int to imgui.input_text with InputTextFlags_.chars_decimal, while keeping the old ValueError → min_value fallback. In the new free-text path, this fallback triggers on user-typed buffers that int() rejects, silently overwriting the prior value with the minimum.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/knx-gui/src/knx_gui/widgets/parameter_widgets.py at _render_int_param and trace its callers for NumberWidget and NumberSliderWidget. Verify that unparseable input is rejected without changing the prior bound value or triggering on_change, while valid integers still receive the existing min/max clamping.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 1/5
- Estimated time
- Under an hour
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 86/100