DEV Community

Cover image for I Already Had Sentry. Then an iPhone from 2018 White-Screened My Karaoke App.
Lê Huy Giang
Lê Huy Giang Subscriber

Posted on

I Already Had Sentry. Then an iPhone from 2018 White-Screened My Karaoke App.

Summer Bug Smash: Clear the Lineup 🐛🛹

This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry.

Project Overview

VKara is a karaoke room in the browser.

YouTube already plays the videos. VKara's job is the room: one TV as the playback screen, everyone's phone as a remote.

I rebuilt that for the GitHub Finish-Up-A-Thon. The idea is simple enough to explain in one sentence. The users are not.

A karaoke night at home usually involves parents, uncles, and aunts. People who are not going to update their phones. People whose "second phone" is still Safari 12. The TV in the living room is often a Tizen or Android TV from years ago. I do not own every one of those devices. I cannot sit in every living room and tap through first paint.

That is why I added Sentry about a month ago — as foundation, not as a challenge badge. vkara-web and vkara-api. Real production. The point was to see what old phones and old TVs do when I am not in the room.

Bug Fix or Performance Improvement

Sentry's issue list is noisy. That is normal.

Some events are not VKara bugs. YouTube's iframe hitting a Tizen same-origin wall. Zalo — a chat app a lot of people in Vietnam use — injecting zaloJSV2 into the page. I left those alone.

Two first-party failures were taking the room down. (Sentry issue links need org login. The screenshots below are the public proof.)

White screen on iOS 12 — WEB-B, WEB-C.

Session Replay: blank vkara.vercel.app, 19s timeline with error markers. Activity: Intl.PluralRules (WEB-B) then addEventListener is not a function (WEB-C)
19 seconds. White screen. WEB-B and WEB-C in one Replay.

This is the device I would never have tested on purpose. Mobile Safari 12.1.2. iOS 12.5.8. Referrer: Yahoo Vietnam.

Replay showed the whole night dying in under 20 seconds. next-intl constructs Intl.PluralRules. Then matchMedia().addEventListener. Then the error boundary called useI18n() to explain the crash — same Intl path. The safety net crashed too.

Sentry VKARA-WEB-B: TypeError new Intl.PluralRules. Mobile Safari 12.1.2 / iOS 12.5.8. Seer: PluralRules not supported on Safari 12
Seer named the missing API. The error-boundary re-throw I found in error.tsx.

Related shelf 502 — API-A, API-C.

Sentry VKARA-API-A: attributedDescription.content undefined. 189 events, POST /related, bun 1.3.13 production
POST /related. InnerTube HTTP succeeded. youtubei threw on schema drift. Users=1 is a proxy IP. 189 events at screenshot time.

The phone remote asks for related songs. The parser hit a missing YouTube field. The route turned that into 502 youtube_upstream_failed. The player could still be up. The remote felt broken.

No new features. I wanted those two to stop killing a karaoke night.

I am not going to quote "zero events since deploy."

Safari 12 is a long tail. Two days of silence and two months of silence can mean the same thing: that phone did not open the room again. Supporting old devices needs users I do not have on a contest deadline. A quiet dashboard is not a result.

The number that matters is what one event does to the room.

WEB-B and WEB-C: 9 events, 2 users, last seen 6 Aug. Looks small. The Replay is 19 seconds of white screen. One phone opening the room is enough to take the night with it. Last-seen-in-August is how rare the device is, not how small the bug is.

API-A: 189 events at triage. Users=1 is a proxy. That count is how often /related turned a parser throw into 502 while a song was already playing. I did not try to make the Sentry issue disappear. youtubei will keep drifting. After the fix, the same throw is kind=parse, handled, warning. The remote gets 200 and an empty shelf. Local check: real videoId → 20 items; bad continuation → empty list, not 502.

That is the impact I can defend before the next iPhone from 2018 shows up.

Code

PR: https://github.com/lehuygiang28/vkara/pull/8

Fixes VKARA-WEB-B · WEB-C · API-A · API-C

I already had tv-polyfills.js for Tizen / Chrome 76. It loads beforeInteractive on every locale. Production showed Safari 12 needed two more shims. Native APIs stay untouched:

if (typeof Intl !== 'undefined' && typeof Intl.PluralRules !== 'function') {
    Intl.PluralRules = function PluralRules(locale) {
        this.locale = locale == null ? 'en' : String(locale);
    };
    Intl.PluralRules.prototype.select = function select() {
        return 'other';
    };
}
Enter fullscreen mode Exit fullscreen mode

I did not ship full CLDR plurals. Vietnamese does not need English-style plurals. A white screen is worse than select() returning 'other'.

The error boundary no longer imports next-intl:

export default function LocaleError({ error, reset }) {
    const phase = useErrorBoundaryRecovery(error, reset);
    return <RecoveryShell phase={phase} label={documentRecoveryPhaseLabel(phase)} />;
}
Enter fullscreen mode Exit fullscreen mode

Related parse failures become an empty list. Sentry still gets kind=parse. Real YouTube HTTP failures still 502:

export function safeParseRelated(raw, client) {
    try {
        return BaseVideoParser.parseRelated(asYoutubeRawData(raw), client);
    } catch (error) {
        captureRelatedParseFailure(error);
        return [];
    }
}
Enter fullscreen mode Exit fullscreen mode

My Improvements

The useful decision was what not to fix.

I did not chase Zalo inject or Tizen iframe SOP. Those show up in Sentry. They are not my product.

I did not wrap every matchMedia call with addListener. There are two call sites today. There will be a third. One prototype shim in the file that already runs before app JS is enough.

I did not keep useI18n() behind try/catch. Hooks cannot do that. The recovery label is a small en / vi map from document.documentElement.lang. Ugly. Correct.

I did not turn every /related error into 200. An empty shelf looks like "no recommendations." A 502 looks like the app is down. During a song, those are not the same thing.

What I learned: nobody at a karaoke night cares that Intl.PluralRules shipped in Safari 13.

They care that the TV shows a video, and related still returns a list.

Best Use of Sentry

A month ago I wired Sentry as production foundation because VKara runs on devices I do not have on my desk: old iPhones, old Samsung TVs, Android TV boxes that never get a browser update. If something white-screens in a living room, I only find out if the client tells me.

What surprised me was the community support. Node is obvious. Bun, I expected they would get to. Seeing a first-party Sentry guide for Elysia still caught me. That is the kind of coverage that makes foundation work boring in a good way: Sentry.init(), wrap the app, ship. I was not writing a custom adapter for a karaoke API.

This challenge is the first time that bet paid off in a way I can point at.

Tool What I actually used it for
Error monitoring WEB-B: Safari 12.1.2 / iOS 12.5.8 — the phone I do not own. API-A: 189 events at triage, kept as handled parse
Session Replay WEB-B and WEB-C are one white screen, not two stories
Seer Named Intl.PluralRules. Missed that error.tsx would re-throw
MCP From the editor: pull API-A, keep WEB-A / Zalo off the PR

Seer first returned HTTP 402. No budget on the plan I had. I used bugsmash26, then WEB-B RCA ran on the issue page.

Same reason there's no before/after chart above — a quiet week doesn't prove a long-tail device is fixed. What Sentry proved instead: the 19 seconds, and that API-A now reports as kind=parse instead of 502.

Best Use of Google AI

I used Antigravity with Gemini and the Sentry MCP. Not to generate the PR. To sit next to the issue list and ask what was actually mine.


Connect to org vkara, find the issue, explain cause.

The Sentry MCP setup page currently shows Gemini CLI. Antigravity wants serverUrl instead of url. One field. Then it connected. If Sentry adds an Antigravity snippet later, that extra minute goes away. Not a real problem.

{
  "mcpServers": {
    "sentry": {
      "serverUrl": "https://mcp.sentry.dev/mcp"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Gemini + MCP pulled Seer on API-A: InnerTube schema drift, videoInfo.attributedDescription undefined inside youtubei Video.load. That is the /related 502.

It also showed WEB-A (YouTube iframe SOP) and WEB-9 (Zalo WebView inject). I did not patch those.

That was the useful part.

In the Finish-Up-A-Thon I learned not to ask AI to dump code. Same here. Gemini helped me triage. The shim, the error-boundary helper, the parse wrapper, and the tests are mine.

Sentry showed the crash on a phone I do not own.

Gemini helped me not fix the wrong crash.

Then I made the smallest change I would still trust on a weekend night.

Top comments (0)