Skip to content

HTTP client methods never call error_for_status — 4xx/5xx responses are treated as success #421

Description

@forkwright

Finding

Several syndesmos HTTP client methods send a request and discard the response without checking its status. reqwest's send() returns Err only for transport-level failures; HTTP 401, 403, 404, 429, and 5xx all resolve to Ok(Response) and are then dropped, after which the method returns Ok(()). The defect is present in LastfmClient::submit_scrobble and PlexClient::refresh_library_section, and the same .send().await.context(...)?-without-.error_for_status() shape applies to the other request sites (e.g. fetch_favorites).

Evidence

crates/syndesmos/src/lastfm/mod.rs:99-101:

.send()
.await
.context(LastfmApiCallSnafu)?;

crates/syndesmos/src/plex/mod.rs:60:

.send()
.await
.context(PlexApiCallSnafu)?;

In both cases no .error_for_status() (or error_for_status_ref()) call sits between send() and the response being dropped.

Why this matters

Error responses are indistinguishable from success. A 401 from a rotated/expired Last.fm session key or Plex token silently discards every scrobble / library-refresh; a 429 rate-limit is ignored. Because no Err is produced, the circuit breaker never trips, retries never run, and nothing reaches the logs — the integration appears healthy while fully degraded. Silent suppression of remote-rejection state is exactly the failure that hides an interrupted or tampered upstream from the operator.

Desired correction

Insert .error_for_status() (or error_for_status_ref()) after .send().await at each site and map the resulting reqwest::Error through the existing *ApiCallSnafu context — submit_scrobble, refresh_library_section, and fetch_favorites.
Done when: a mock-HTTP test feeding a 401 to submit_scrobble returns Err(LastfmApiCall { .. }) and registers a circuit-breaker failure, and the equivalent test for refresh_library_section returns Err(PlexApiCall { .. }).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions