Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions findmy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ class UnhandledProtocolError(RuntimeError):
"""


class EmptyResponseError(RuntimeError):
"""
Raised when Apple servers return an empty response when querying location reports.

This is a bug on Apple's side. More info: https://github.com/malmeloo/FindMy.py/issues/185
"""


class InvalidStateError(RuntimeError):
"""
Raised when a method is used that is in conflict with the internal account state.
Expand Down
11 changes: 9 additions & 2 deletions findmy/reports/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from findmy import util
from findmy.errors import (
EmptyResponseError,
InvalidCredentialsError,
InvalidStateError,
UnauthorizedError,
Expand Down Expand Up @@ -669,8 +670,14 @@ async def _do_request() -> util.http.HttpResponse:
retry_counter += 1

if retry_counter > 3:
logger.warning("Max retries reached, returning empty response")
return resp
logger.warning(
"Max retries reached, returning empty response. \
Location reports might be missing!"
)
msg = "Empty response received from Apple servers. \
This is most likely a bug on Apple's side. \
More info: https://github.com/malmeloo/FindMy.py/issues/185"
raise EmptyResponseError(msg)

await asyncio.sleep(2)

Expand Down
18 changes: 14 additions & 4 deletions findmy/reports/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from findmy import util
from findmy.accessory import RollingKeyPairSource
from findmy.errors import EmptyResponseError
from findmy.keys import HasHashedPublicKey, KeyPair, KeyPairMapping, KeyPairType

if TYPE_CHECKING:
Expand Down Expand Up @@ -417,7 +418,7 @@ async def fetch_location_history(

return reports

async def _fetch_accessory_reports(
async def _fetch_accessory_reports( # noqa: C901
self,
accessory: RollingKeyPairSource,
only_latest: bool = False,
Expand Down Expand Up @@ -480,7 +481,10 @@ async def _fetch() -> set[LocationReport]:
len(cur_keys_primary | new_keys_primary) > 290
or len(cur_keys_secondary | new_keys_secondary) > 290
):
ret |= await _fetch()
try:
ret |= await _fetch()
except EmptyResponseError:
return []

# if we only want the latest report, we can stop here
# since we are iterating backwards in time
Expand All @@ -498,7 +502,10 @@ async def _fetch() -> set[LocationReport]:

if cur_keys_primary or cur_keys_secondary:
# fetch remaining keys
ret |= await _fetch()
try:
ret |= await _fetch()
except EmptyResponseError:
return []

return sorted(ret)

Expand All @@ -510,7 +517,10 @@ async def _fetch_key_reports(

# fetch all as primary keys
ids = [([key.hashed_adv_key_b64], []) for key in keys]
encrypted_reports: list[LocationReport] = await self._account.fetch_raw_reports(ids)
try:
encrypted_reports: list[LocationReport] = await self._account.fetch_raw_reports(ids)
except EmptyResponseError:
encrypted_reports = []

id_to_key: dict[bytes, HasHashedPublicKey] = {key.hashed_adv_key_bytes: key for key in keys}
reports: dict[HasHashedPublicKey, list[LocationReport]] = {key: [] for key in keys}
Expand Down