GeezerVison is showing github.com — Paginate list responses when multi requests are disabled by DevilRange · Pull Request #1838 · python-kasa/python-kasa
Conversation
DevilRange
commented
Nov 5, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Fixes #1804. Probably also #1789 (same symptom on a KH100, no logs there to confirm).
One timeout on a batched request sets _multi_request_batch_size = 1 for the life of the protocol (#1759). _execute_multiple_query then takes the step == 1 branch, which never calls _handle_response_lists, so every list stops at its first page.
On a hub with more than 10 children, get_child_device_list returns 10 entries and the rest are removed as if unpaired, until the protocol is recreated. In Home Assistant the devices are deleted from the registry until the integration is reloaded (core#185809). #1802 stopped this for smartcam hubs by keeping batching on. SmartProtocol hubs (H100, KH100) still hit it.
Change
Drop the separate step == 1 send loop. With batching off, the requests go through the existing loop at the end of the method, which now paginates. That loop also handles lists requeried after an error in a batch, which were truncated the same way with no timeout involved.
- A failed page raises instead of returning a short list, so no children are removed.
- Complete lists cost no extra requests, so the P300 from Disable multi requests on timeout during multi-request #1759 is unaffected.
- The one-way switch to single requests stays. The
batch_size=1 config and the JSON decode fallback reach the same path, so pagination has to work here regardless.
- A
FORCE_SINGLE_REQUEST method no longer leaves an empty multipleRequest after the last batch (from ZeliardM's review). On SmartCamProtocol a lone scanApList or connectAp sent one on every call.
Testing
Ran this method on an H100 (firmware 1.7.10, 13 children) on 0.12.0. A real timeout logged Timeout during multi request, multi requests disabled. Requests with start_index=10 kept going, all 13 children stayed, and nothing went unavailable in the 9 minutes I logged after it. On stock 0.12.0 the same hub lost its last 3 children within seconds of that log line, and page 2 was never requested again.
The new parametrization and the two new tests fail without the change.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
After a timeout disables multi requests, _execute_multiple_query returned
from a separate single request branch that never fetched further list pages.
A hub with more than 10 children then lost the children past the first page
until the protocol was recreated.
Remove that branch and send the requests from the existing loop at the end of
the method, which now paginates. Lists requeried after an error in a batch had
the same truncation and are fixed by this too.
codecov
Bot
commented
Nov 5, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.49%. Comparing base (9478ed2) to head (185b022).
⚠️ Report is 1 commits behind head on master.
Additional details and impacted files
@@ Coverage Diff @@
## master #1838 +/- ##
==========================================
- Coverage 93.49% 93.49% -0.01%
==========================================
Files 161 161
Lines 10169 10166 -3
Branches 1053 1053
==========================================
- Hits 9508 9505 -3
Misses 469 469
Partials 192 192
☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.
🚀 New features to boost your workflow:
- ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Nov 5, 2026
Comment on lines
+225
to
+228
|
|
step = self._multi_request_batch_size
|
|
|
if step == 1:
|
|
|
# If step is 1 do not send request batches
|
|
|
for request in multi_requests:
|
|
|
method = request["method"]
|
|
|
req = self.get_smart_request(method, request.get("params"))
|
|
|
resp = await self._transport.send(req)
|
|
|
self._handle_response_error_code(
|
|
|
resp, method, raise_on_error=raise_on_error
|
|
|
)
|
|
|
multi_result[method] = resp["result"]
|
|
|
return multi_result
|
|
|
|
|
|
for batch_num, i in enumerate(range(0, end, step)):
|
|
|
# If step is 1 do not send batches, the loop at the end sends single requests
|
|
|
batch_starts = range(0, end, step) if step != 1 else ()
|
|
|
for batch_num, i in enumerate(batch_starts):
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
step = self._multi_request_batch_size |
|
if step == 1: |
|
# If step is 1 do not send request batches |
|
for request in multi_requests: |
|
method = request["method"] |
|
req = self.get_smart_request(method, request.get("params")) |
|
resp = await self._transport.send(req) |
|
self._handle_response_error_code( |
|
resp, method, raise_on_error=raise_on_error |
|
) |
|
multi_result[method] = resp["result"] |
|
return multi_result |
|
|
|
for batch_num, i in enumerate(range(0, end, step)): |
|
# If step is 1 do not send batches, the loop at the end sends single requests |
|
batch_starts = range(0, end, step) if step != 1 else () |
|
for batch_num, i in enumerate(batch_starts): |
|
step = self._multi_request_batch_size |
|
if step == 1: |
|
# Batching is off, so the loop at the end sends each request on its own |
|
multi_requests = [] |
|
for batch_num, i in enumerate(range(0, len(multi_requests), step)): |
This might read a little plainer than the conditional range. Counting to len(multi_requests) instead of end also stops an empty multipleRequest from going out when a FORCE_SINGLE_REQUEST method is in the query (5 methods plus scanApList at batch size 5, for example), which happens on master too. The full suite passes with it.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied, thanks. Same thing on SmartCamProtocol for a lone scanApList or connectAp: end is 1 and multi_requests is empty, so wifi_scan and wifi_join sent an empty multipleRequest first. Added a test for the 5 + scanApList case. Checked against the fake transport only, not a real camera.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
The batch loop counted FORCE_SINGLE_REQUEST methods, so it could send an empty
batch. On SmartCamProtocol a lone scanApList or connectAp always sent one.
Co-authored-by: ZeliardM <[email protected]>
|
Another H100 confirmation, on python-kasa 0.12.0 / Home Assistant 2026.11.0.
The hub has 16 children. After Timeout during multi request, multi requests disabled, every update returns start_index 0, sum 16 with only 10 entries, and the same 6 children are removed (Removed child device ...) with no warning or error logged. Page 2 is never requested again until the config entry is reloaded. In about 25 hours the hub lost them roughly 70 times, every 1 to 5 minutes during the night, and each time an automation had to reload the entry. My three other hubs have 6 children each and never lose any.
This matches the analysis in #1804, and the change in this PR targets exactly the step == 1 branch where it happens. I'd be glad to see it merged and released so Home Assistant can pick it up.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Labels
bug
Something isn't working
Milestone
No milestone
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|