Skip to content

FCM OAuth success-body Parse/TooLarge is classified Permanent, dropping the push with no retry — the sibling variants #248 left uncovered #330

Description

@erskingardner

Low-probability edge case: a clean-EOF partial-JSON or interstitial 200 body is uncommon; the far more common mid-body drop already surfaces as Read (correctly retriable since #248).

Summary

In FcmTokenGenerator::mint, the OAuth token success body (HTTP 200) is read via parse_bounded_json_body. #248 fixed the BoundedJsonBodyError::Read variant to map to oauth_error_from_transportRetriable. But the two sibling variants of the same read, Parse and TooLarge, still map to TokenAcquisitionError::permanent(...). A transient garbage-200 therefore drops the current FCM push with no retry (and, if persistent, fails every FCM push while recording a provider hard failure).

Location

  • src/push/fcm.rs:397-406 — the TooLarge and Parse arms of the success-body match in mint
Err(crate::push::BoundedJsonBodyError::TooLarge) => {
    return Err(TokenAcquisitionError::permanent(Error::Fcm(...)));
}
Err(crate::push::BoundedJsonBodyError::Parse) => {
    return Err(TokenAcquisitionError::permanent(Error::Fcm(...)));
}

Details

Permanentinto_send_attempt()SendAttemptResult::Permanentwith_retry returns Err immediately with no retry → the notification is dropped and the dispatcher records a provider hard delivery failure.

Parse can arise transiently rather than from a genuinely malformed Google response:

  • an intercepting proxy / captive portal returning a 200 with an HTML interstitial body, or
  • a chunked-transfer success stream that ends cleanly (chunk()Ok(None)) after only partial JSON — a clean-EOF truncation surfaces as Parse, not Read.

TooLarge (>MAX_OAUTH_BODY_BYTES) is even less likely for a real OAuth response but has the same drop-without-retry consequence.

Because this is the mint path shared by every FCM send, a persistent garbage-200 also fails all FCM pushes.

Why this is not a duplicate

#248 resolved only the Read/IO variant of this exact success-body read. This is its two uncovered siblings. #277 is about OAuth non-2xx status classification, a different branch.

Recommendation

Map Parse/TooLarge on the OAuth success body to Retriable (consistent with #248's treatment of Read), so a transient interstitial/truncated 200 is retried rather than dropped. A genuinely malformed provider response will simply exhaust the retry budget and then fail.

Impact

One dropped FCM push per occurrence (or total FCM outage if persistent), gated on a rare transient-200 shape. LOW.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions