⚠️ This issue is AI-generated (Claude Opus 4.8 via Claude Code) on behalf of tobixen
Completing a recurring VTODO whose RRULE contains a COUNT= crashes in todo_complete().
In calendar_cli/legacy.py (around line 684):
count_search = re.search(r'COUNT=(\d+)', completed_task.instance.vtodo.rrule.value)
if count_search:
remaining_task.instance.vtodo.rrule.value = re.replace(r'COUNT=(\d+)', 'COUNT=%d' % int(count_search.group(1))-1)
That single line has three independent bugs:
re.replace does not exist. The re module has no replace function — the intended call is re.sub. This alone raises AttributeError.
- The subject string is missing.
re.sub takes (pattern, repl, string), but the rrule value to operate on is never passed.
- Operator-precedence bug.
'COUNT=%d' % int(...) - 1 parses as ('COUNT=%d' % int(...)) - 1, i.e. str - int, which raises TypeError. The intent was to decrement inside the parentheses: int(...) - 1.
The net effect: any code path that reaches this branch — completing a recurring task whose RRULE carries a COUNT, in order to decrement the remaining occurrences — raises an exception.
Likely intended implementation:
if count_search:
new_count = int(count_search.group(1)) - 1
remaining_task.instance.vtodo.rrule.value = re.sub(
r'COUNT=(\d+)',
'COUNT=%d' % new_count,
remaining_task.instance.vtodo.rrule.value,
)
There is currently no test covering completion of recurring todos with a COUNT; a regression test should accompany the fix.
Context: surfaced by ruff during project modernization. The W605 invalid-escape warnings on these regex literals were already fixed (raw strings); the logic bugs above were intentionally left out of scope for that change.
⚠️ This issue is AI-generated (Claude Opus 4.8 via Claude Code) on behalf of tobixen
Completing a recurring
VTODOwhoseRRULEcontains aCOUNT=crashes intodo_complete().In
calendar_cli/legacy.py(around line 684):That single line has three independent bugs:
re.replacedoes not exist. Theremodule has noreplacefunction — the intended call isre.sub. This alone raisesAttributeError.re.subtakes(pattern, repl, string), but the rrule value to operate on is never passed.'COUNT=%d' % int(...) - 1parses as('COUNT=%d' % int(...)) - 1, i.e.str - int, which raisesTypeError. The intent was to decrement inside the parentheses:int(...) - 1.The net effect: any code path that reaches this branch — completing a recurring task whose
RRULEcarries aCOUNT, in order to decrement the remaining occurrences — raises an exception.Likely intended implementation:
There is currently no test covering completion of recurring todos with a
COUNT; a regression test should accompany the fix.Context: surfaced by ruff during project modernization. The
W605invalid-escape warnings on these regex literals were already fixed (raw strings); the logic bugs above were intentionally left out of scope for that change.