Skip to content

Bodyless Bluesky token refresh; one platform's failure no longer 500s post metrics - #316

Open
gynsus wants to merge 1 commit into
trypostit:mainfrom
gynsus:pr/metrics-resilience
Open

Bodyless Bluesky token refresh; one platform's failure no longer 500s post metrics#316
gynsus wants to merge 1 commit into
trypostit:mainfrom
gynsus:pr/metrics-resilience

Conversation

@gynsus

@gynsus gynsus commented Aug 30, 2026

Copy link
Copy Markdown

Two fixes from one production incident: the get-post-metrics MCP tool (and the web post-analytics panel behind the same PostMetricsFetcher) returned HTTP 500 for a multi-platform post.

Fix 1: bsky.social rejects the refresh call TryPost sends

Laravel's Http::post($url) with no data still sends an empty JSON body ({} with Content-Type: application/json). bsky.social now rejects com.atproto.server.refreshSession called that way:

A request body was provided when none was expected

so every Bluesky token refresh failed, the account flapped, and refresh kept being retried. The call now goes through send('POST', …), which sends no body at all. BlueskyRefreshSessionTest pins the contract ($request->body() === '') so a helper refactor can't quietly reintroduce the body.

Fix 2: one platform's exception nuked the whole metrics aggregate

PostMetricsFetcher::forPlatform() let provider exceptions propagate, so a single misbehaving platform (here: Bluesky mid-token-flap) turned "metrics for this post" into a 500 for every platform of the post — the MCP tool, the REST endpoint, and the web panel all share this path. Per-platform fetches are now wrapped: a failure logs a warning and degrades that one platform to ['unsupported' => true, 'reason' => 'error'], the shape the frontend already renders as "metrics unavailable".

Testing

New BlueskyRefreshSessionTest and a PostMetricsFetcherTest case for the degrade path; ConnectionVerifierTest passes. Verified live: the failing post's metrics now render for all healthy platforms, and Bluesky refresh succeeds against bsky.social.

…efresh

Two halves of the same outage. bsky.social started rejecting
refreshSession calls that carry a body — a data-less post() still sends
an empty JSON body — so the scheduled refresh failed every cycle and the
account limped along on the credential re-auth fallback, leaving windows
with a dead access token. During those windows the analytics service
threw out of PostMetricsFetcher's match and a single provider's exception
turned the whole get-post-metrics aggregate into a 500 for every post.

The refresh now uses send('POST') with no body, and forPlatform wraps
each provider in a try/catch that logs which platform failed and returns
an unsupported/error entry so the other platforms still report.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant