Another in the series of redacted-format engagement reports, this one on omny.info, MTA's account management portal for OMNY, the tap-to-pay fare system covering New York City's subway and bus network. The target here isn't a GitHub repo. It's a live production system that handles fare payment for millions of daily riders plus tourists, so every claim in this report is held to a higher bar than usual: nothing here was asserted without being independently confirmed against real, directly-fetched code, on both sides of the dependency chain.
Scope and Objective
Target: the client-side submission paths for two account workflows on omny.info: updating a phone number in Account Settings, and loading or adding a fare card.
Objective: confirm the actual root cause of a reproducible UI hang on both flows, rather than accept a plausible-sounding guess at what the code does. Verification meant fetching OMNY's own publicly-served frontend bundle directly (the same JavaScript already sent to every visitor's browser, nothing authenticated or non-public touched), and separately cloning and reading the real, open-source library implicated in the failure, to determine precisely which side of the dependency the fault sits on.
Findings
Core account and payment transactions hang on a blocked analytics call (High)
Attempting to update a phone number, or load/add a fare card, on omny.info fails silently when the browser blocks outbound requests to the domain PostHog's bundled SDK calls out to. A standard privacy-extension "strict" tracking-protection mode is enough to trigger it. Console output on both failures:
posthog.0482b028.js:14 Enqueued failed request for retry in 26927
POST https://pypestream.com net::ERR_BLOCKED_BY_CLIENT
posthog.0482b028.js:15 Bad HTTP status: 0
posthog.0482b028.js:14 Enqueued failed request for retry in 14257
POST https://pypestream.com net::ERR_BLOCKED_BY_CLIENT
Same script, same hash, same blocked call, on both the phone-number flow and the card-loading flow: one shared root cause.
A correction: the blocked domain, pypestream.com, is misattributed as the analytics vendor. OMNY's real client bundle (fetched directly and confirmed across two separately-built deployments, client.197e4ff5.js and client.9209a1c2.js) shows Pypestream is a distinct integration, a customer-support chat widget (PYPESTREAM_CHATBOT_ELEMENT_ID, loaded from web.pypestream.com/launcher.js). Neither bundle contains the string "posthog" anywhere. The call to pypestream.com's domain comes from PostHog's own code, almost certainly a configured event-forwarding destination.
Ruling out the retry mechanism itself
The obvious read of that console output is "PostHog is stuck in an infinite retry loop." PostHog's client SDK, posthog-js, is open source, so this doesn't need to stay a guess. Cloned the real repository and read the actual file:
$ git clone --depth 50 https://github.com/PostHog/posthog-js.git
$ cd posthog-js && git grep -n "Enqueued failed request for retry"
packages/browser/src/retry-queue.ts:129: let logMessage = `Enqueued failed request for retry in ${msToNextRetry}`
Reading retry-queue.ts directly: the queue is scheduled with setTimeout (asynchronous, never blocks other code running on the page) and bounded by a hard constant, STATUS_CODE_ZERO_MAX_RETRIES = 3, specifically for the blocked-request/CORS/ad-blocker case this bug hits. After three attempts it logs "Stopped retrying" and gives up on its own. That rules out a vendor-side defect as the cause. A well-behaved, bounded, non-blocking retry queue cannot, by itself, freeze a page.
Root cause: OMNY gates its own transaction on a telemetry call it doesn't control (High, CWE-754)
With PostHog's side confirmed clean, the fault is OMNY's own integration. Somewhere in the submission handlers for both flows, the real backend transaction, the profile update, the card load, is made to wait on that PostHog capture call succeeding. PostHog does exactly what a telemetry SDK should: tries, retries a bounded number of times, gives up gracefully. OMNY's code has no path forward once that happens, and the user is left staring at a hang with no error shown.
This is CWE-754, Improper Check for Unusual or Exceptional Conditions, verified against MITRE's definition. The code assumes a third-party telemetry call will eventually succeed and has no handling for the ordinary case where it doesn't.
Impact
This fails closed on the wrong dependency. A non-critical integration (analytics, routed through a support-chat vendor's domain for reasons still unexplained) is architected as a hard blocker for a security-relevant transaction (contact info tied to account recovery) and a financial one (loading fare payment). It penalizes users practicing ordinary browser hygiene: anyone running a privacy extension loses the ability to secure their account at precisely the moment that matters, and loses the ability to load a transit card.
The victim doesn't need a privacy extension of their own for this to be used against them. OMNY's own code is what gates the transaction on pypestream.com, not the attacker's, so anything that keeps that one domain from resolving or responding reproduces the same lockout. A phishing page or malicious ad/iframe could block that specific request client-side; an attacker on the victim's network path (a rogue hotspot, a compromised local DNS resolver) could drop it at the network layer. Neither was tested here — this is a plausible path on a confirmed root cause, flagged as such, not a tested exploit.
Mitigation
Decouple the PostHog capture call from the transaction path entirely. Fire the event, catch and log any rejection locally, and never let the real api.updateProfile() or card-loading backend call wait on, or be gated by, the telemetry call's outcome. PostHog's own SDK already fails gracefully after three attempts. OMNY's integration needs to do the same instead of treating that graceful failure as a reason to stop.
A second, unrelated failure on the same form (High)
Live-testing the phone-number edit flow directly, with the privacy-extension block confirmed active, surfaced a different failure. Every submission failed, regardless of what number was entered, three independent attempts, three different numbers, with a visible "Unexpected server error" instead of the silent hang above.
Console showed GoogleAnalytics.logException immediately before each failure, the same shape as the PostHog finding, analytics being blocked. The real network log told a different story: zero requests to any Google domain, zero to PostHog or Pypestream, fired during this flow. The actual GraphQL mutation POST returns HTTP 200, succeeding at the transport level. A translation lookup fires immediately after for the key miscText.error-backoffice-customer-contact-update-unknown-field, OMNY's own backend naming its own failure: the customer-contact-update mutation is rejected server-side with an unknown-field validation error. GoogleAnalytics.logException is OMNY's own error handler logging the caught exception, a symptom of the failure.
This explains why PIN creation and PIN change, tested the same session, both succeeded cleanly: a different mutation, one that doesn't touch the mismatched field. Two confirmed, independent, root-cause-distinct bugs on the same portal. This second one is the harder failure: it blocks every account holder from updating contact info right now, for any value, with or without a privacy extension.