Skip to content

[mobile] Diagnose mobile tRPC non-JSON responses - #1252

Merged
Asherlc merged 2 commits into
mainfrom
Asherlc/sentry-7371799432
Jun 5, 2026
Merged

Asherlc merged 2 commits into
mainfrom
Asherlc/sentry-7371799432

Conversation

@Asherlc

@Asherlc Asherlc commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Adds a mobile tRPC fetch wrapper that fails before JSON parsing when the server returns a non-JSON response. The thrown error includes the tRPC path, HTTP status, and a short body preview so Sentry can show the real upstream failure for WHOOP BLE background uploads. Documents the production incident baseline and current remaining risk.

Summary by Sourcery

Add a mobile tRPC fetch wrapper that surfaces non-JSON responses for WHOOP BLE uploads and document the related production incident and diagnostic mitigation.

New Features:

  • Introduce a mobile tRPC fetch wrapper that validates JSON responses and throws diagnostic errors for non-JSON tRPC responses before JSON parsing.

Enhancements:

  • Wire the mobile tRPC client to use the new fetch wrapper so WHOOP BLE background uploads report detailed response diagnostics.

Documentation:

  • Document the 2026-06-04 mobile WHOOP BLE tRPC non-JSON response incident, including symptoms, impact, mitigation, and remaining risk.

Summary by cubic

Adds a mobile tRPC fetch wrapper that fails early on non-JSON responses and throws a clear error with the tRPC path, HTTP status, content type, and a short body preview. This lets Sentry show the real upstream error during WHOOP BLE background uploads.

  • New Features
    • Added createTrpcFetch in packages/mobile/lib/trpc-fetch.ts; JSON responses pass through unchanged.
    • Handles relative tRPC URLs, absent content-type, and keeps diagnostics if the body preview can’t be read.
    • Wired into the mobile tRPC client in packages/mobile/app/_layout.tsx, with unit tests in packages/mobile/lib/trpc-fetch.test.ts, and incident details in docs/production-incident-baseline.md.

Written for commit 5725970. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error handling for mobile uploads that fail due to non-JSON responses. Users now receive clearer diagnostic messages including HTTP status and response details for improved troubleshooting.
  • Documentation

    • Added production incident baseline documentation detailing past mobile upload failures and mitigation strategies.

Copilot AI review requested due to automatic review settings June 4, 2026 23:11
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@sourcery-ai

sourcery-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a diagnostic tRPC fetch wrapper for the mobile app that intercepts non-JSON responses before JSON parsing, throws an error with path/status/body preview for Sentry visibility, wires it into the mobile tRPC client, and documents the related production incident and remaining risk.

Sequence diagram for the new mobile tRPC fetch wrapper handling non-JSON responses

sequenceDiagram
  participant MobileApp
  participant TrpcClient
  participant TrpcFetchWrapper as createTrpcFetch
  participant FetchImpl as fetchImpl
  participant Server

  MobileApp->>TrpcClient: tRPC request
  TrpcClient->>TrpcFetchWrapper: fetch(input, init)
  TrpcFetchWrapper->>FetchImpl: fetchImpl(input, init)
  FetchImpl->>Server: HTTP request
  Server-->>FetchImpl: HTTP response
  FetchImpl-->>TrpcFetchWrapper: Response

  alt JSON response
    TrpcFetchWrapper-->>TrpcClient: Response (unchanged)
    TrpcClient-->>MobileApp: tRPC result
  else Non_JSON_response
    TrpcFetchWrapper->>TrpcFetchWrapper: getTrpcPath(input)
    TrpcFetchWrapper->>TrpcFetchWrapper: buildStatusLabel(Response)
    TrpcFetchWrapper->>TrpcFetchWrapper: Response.clone().text()
    TrpcFetchWrapper-->>TrpcClient: Error("Non-JSON tRPC response from ...")
    TrpcClient-->>MobileApp: Surface error for Sentry
  end
Loading

File-Level Changes

Change Details Files
Introduce a mobile tRPC fetch wrapper that detects and surfaces non-JSON responses before JSON parsing.
  • Define helpers to inspect content-type, detect JSON responses, and extract the tRPC path from the request URL.
  • Wrap the underlying fetch implementation to return JSON responses unchanged but, for non-JSON responses, read a short cloned-body preview and throw an Error including path, HTTP status label, and preview.
  • Expose the wrapper via createTrpcFetch(fetchImpl?) so it can be injected and is testable.
packages/mobile/lib/trpc-fetch.ts
Wire the custom tRPC fetch into the mobile app tRPC client configuration.
  • Update the mobile AuthGate tRPC client configuration to pass fetch: createTrpcFetch() in commonOptions so all mobile tRPC calls go through the diagnostic wrapper.
packages/mobile/app/_layout.tsx
Document the WHOOP BLE tRPC non-JSON response incident and the diagnostic mitigation.
  • Add an incident entry describing symptoms, user impact, evidence, hypothesized behavior of the mobile client on non-JSON responses, and the added diagnostic fetch wrapper.
  • Clarify that the wrapper is diagnostic (not a server-side fix) and should remain to keep future transport regressions observable.
docs/production-incident-baseline.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a mobile-side fetch wrapper that validates tRPC HTTP responses are JSON before parsing, integrates it into the tRPC client configuration in AuthGate, tests the wrapper behavior, and documents the production incident involving non-JSON responses from WHOOP BLE uploads.

Changes

Mobile tRPC JSON validation

Layer / File(s) Summary
tRPC JSON-validation fetch wrapper
packages/mobile/lib/trpc-fetch.ts
Exports createTrpcFetch(), which wraps a fetch implementation and intercepts responses. Returns JSON responses immediately; for non-JSON responses, reads a truncated body preview and throws an error containing the extracted tRPC path, HTTP status/statusText, and body sample.
Wrapper behavior validation
packages/mobile/lib/trpc-fetch.test.ts
Unit tests verify that the wrapper rejects non-JSON responses (HTTP 413 with text/plain) with a detailed error including path and status, and passes through JSON responses unchanged.
tRPC client integration
packages/mobile/app/_layout.tsx
Imports and wires createTrpcFetch into the shared tRPC client configuration so the tRPC instance in AuthGate uses the JSON-validating fetch wrapper.
Production incident documentation
docs/production-incident-baseline.md
Documents a 2026-06-04 incident where mobile WHOOP BLE uploads received non-JSON HTTP responses, causing tRPC JSON parsing to fail. Records the mitigation as the client-side fetch wrapper that surfaces diagnostic information.

🎯 1 (Trivial) | ⏱️ ~8 minutes

area/mobile, type/feature

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed Title follows imperative mood, is 48 chars (under 70), has [mobile] prefix, uses no trailing punctuation, and accurately describes the main change: adding a tRPC fetch wrapper to diagnose non-JSON responses.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Consider making getTrpcPath resilient to relative URLs by either passing a base URL to new URL or wrapping the construction in a try/catch so a malformed/relative input doesn't throw and crash the fetch wrapper.
  • The JSON detection currently relies on content-type including json; if some upstreams return JSON without a proper header, this wrapper will misclassify them as non-JSON—consider allowing a fallback path (e.g., attempting JSON parse on common 2xx responses with missing/ambiguous content types) or at least logging the unexpected header value.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider making `getTrpcPath` resilient to relative URLs by either passing a base URL to `new URL` or wrapping the construction in a try/catch so a malformed/relative `input` doesn't throw and crash the fetch wrapper.
- The JSON detection currently relies on `content-type` including `json`; if some upstreams return JSON without a proper header, this wrapper will misclassify them as non-JSON—consider allowing a fallback path (e.g., attempting JSON parse on common 2xx responses with missing/ambiguous content types) or at least logging the unexpected header value.

## Individual Comments

### Comment 1
<location path="packages/mobile/lib/trpc-fetch.ts" line_range="33-36" />
<code_context>
+      return response;
+    }
+
+    const bodyPreview = (await response.clone().text()).slice(0, BODY_PREVIEW_LIMIT);
+    throw new Error(
+      `Non-JSON tRPC response from ${getTrpcPath(input)}: ${buildStatusLabel(response)} ${bodyPreview}`,
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Guard against failures when reading the response body for the error preview.

If `response.clone().text()` throws (e.g. due to a read error or unsupported body), that error will replace the more informative "Non-JSON tRPC response" error. Wrap the body read in a try/catch and, on failure, fall back to a safe default (e.g. empty or generic preview) while still throwing the tRPC error with status and path.

```suggestion
    let bodyPreview: string;

    try {
      const responseText = await response.clone().text();
      bodyPreview = responseText.slice(0, BODY_PREVIEW_LIMIT);
    } catch {
      bodyPreview = "";
    }

    throw new Error(
      `Non-JSON tRPC response from ${getTrpcPath(input)}: ${buildStatusLabel(response)} ${bodyPreview}`,
    );
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/mobile/lib/trpc-fetch.ts Outdated
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Storybook previews for a829d223 are ready:

This comment updates automatically on each PR push.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Mobile Preview

Scan to open on device:

QR code for dofek://preview/pr-1252

Channel pr-1252
Deep Link dofek://preview/pr-1252
Commit a829d22

To test on device:

  1. Build and install the preview client: PREVIEW_CHANNEL=pr-1252 npx expo prebuild --clean -p ios
  2. Or tap deep link on an existing preview build: dofek://preview/pr-1252

Each PR gets its own channel. Build a preview client with PREVIEW_CHANNEL=pr-{N} to test.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mobile/lib/trpc-fetch.ts`:
- Around line 3-9: getResponseContentType currently returns an empty string when
the header is absent; change its return type to string | null and return null
for missing content-type, then update isJsonResponse to narrow that nullable
return (e.g., check for non-null before calling includes or use a safe optional
check) so it treats absence as null rather than an empty string; update type
annotations for getResponseContentType and adjust isJsonResponse logic
accordingly (referencing getResponseContentType and isJsonResponse).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f8861397-f267-483d-bacb-b384492cc1ff

📥 Commits

Reviewing files that changed from the base of the PR and between a894d20 and 74411ea.

📒 Files selected for processing (4)
  • docs/production-incident-baseline.md
  • packages/mobile/app/_layout.tsx
  • packages/mobile/lib/trpc-fetch.test.ts
  • packages/mobile/lib/trpc-fetch.ts

Comment thread packages/mobile/lib/trpc-fetch.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: This PR adds a small fetch wrapper that detects non-JSON tRPC responses and throws a diagnostic error, along with corresponding tests and incident documentation, all of which are isolated to the mobile app's network layer with no changes to core business logic, security, or infrastructure.

Re-trigger cubic

@Asherlc Asherlc changed the title Diagnose mobile tRPC non-JSON responses [mobile] Diagnose mobile tRPC non-JSON responses Jun 4, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Auto-approved: This PR adds a well-tested tRPC fetch wrapper that only throws on non-JSON responses (passing JSON through unchanged), improving diagnostic capability for mobile uploads without altering core business logic or infrastructure.

Re-trigger cubic

@Asherlc
Asherlc merged commit c41844e into main Jun 5, 2026
66 checks passed
@Asherlc
Asherlc deleted the Asherlc/sentry-7371799432 branch June 5, 2026 00:38
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.

2 participants