Summary
_GarminProxy in src/garmin_mcp/__init__.py collapses every GarminConnectConnectionError into the canned string "Garmin Connect is unreachable. Check your network connection or try again later." and discards the original exception detail. With garminconnect==0.3.2, GarminConnectConnectionError is the catch-all raised for all non-2xx responses (400/404/500), not just genuine connectivity failures — so ordinary client errors are reported to the MCP client as a network outage, and the actionable message is lost.
Where
# src/garmin_mcp/__init__.py
_MESSAGES = {
...
GarminConnectConnectionError: (
"Garmin Connect is unreachable. Check your network connection or try again later."
),
}
...
except tuple(self._MESSAGES) as exc:
for exc_type, msg in self._MESSAGES.items():
if isinstance(exc, exc_type):
raise type(exc)(msg) from None # <-- replaces message, drops str(exc)
raise
Reproduction
Call get_activities_by_date with an activity sub-type (e.g. strength_training) over a date range:
get_activities_by_date(start_date="2025-11-01", end_date="2025-12-31", activity_type="strength_training")
Garmin returns:
GarminConnectConnectionError: API Error 400 - Activity type cannot be an activity sub type
but the tool reports:
Error retrieving activities by date: Garmin Connect is unreachable. Check your network connection or try again later.
The user is told the network is down when in fact they passed an unsupported filter value. The from None also suppresses the traceback chain, so the real cause is unrecoverable from the response.
Impact
Any 4xx/5xx from Garmin (bad filter, not-found, server-side error) is indistinguishable from an outage. This makes tool failures very hard to debug from the MCP client side.
Suggested fix
Preserve the underlying detail (and reword the connection message, since it is not always "unreachable"):
GarminConnectConnectionError: "Garmin Connect request failed",
...
detail = str(exc).strip()
raise type(exc)(f"{msg}: {detail}" if detail else msg) from None
which yields e.g. Garmin Connect request failed: API Error 400 - Activity type cannot be an activity sub type. Auth-expired and rate-limit messages can keep their tailored text but would likewise benefit from appending the detail.
Environment
- garminconnect 0.3.2
- streamable-http transport
Summary
_GarminProxyinsrc/garmin_mcp/__init__.pycollapses everyGarminConnectConnectionErrorinto the canned string "Garmin Connect is unreachable. Check your network connection or try again later." and discards the original exception detail. Withgarminconnect==0.3.2,GarminConnectConnectionErroris the catch-all raised for all non-2xx responses (400/404/500), not just genuine connectivity failures — so ordinary client errors are reported to the MCP client as a network outage, and the actionable message is lost.Where
Reproduction
Call
get_activities_by_datewith an activity sub-type (e.g.strength_training) over a date range:Garmin returns:
but the tool reports:
The user is told the network is down when in fact they passed an unsupported filter value. The
from Nonealso suppresses the traceback chain, so the real cause is unrecoverable from the response.Impact
Any 4xx/5xx from Garmin (bad filter, not-found, server-side error) is indistinguishable from an outage. This makes tool failures very hard to debug from the MCP client side.
Suggested fix
Preserve the underlying detail (and reword the connection message, since it is not always "unreachable"):
which yields e.g.
Garmin Connect request failed: API Error 400 - Activity type cannot be an activity sub type. Auth-expired and rate-limit messages can keep their tailored text but would likewise benefit from appending the detail.Environment