A browser-based Python playground that ships a 5.5 MB runtime began failing silently for users on slow connections. The culprit was a misused Network Information API and an error-grouping dashboard that mislabeled the problem. The bug hid for weeks, wasted developer time, and left a segment of users unable to run code.
How the problem surfaced
The playground’s error tracker flashed a single, eye-catching message: “undefined is not an object.” The title suggested a simple JavaScript typo, so the team chased a non-existent code path. When they inspected the raw metadata, they saw that 89 % of those incidents were actually network timeouts. The dashboard had taken the first error that arrived and used it to name the whole batch, masking the true failure type.
Lesson 1 – Dashboard titles can be deceptive
A dashboard that aggregates incidents helps only if its aggregation logic mirrors the real cause of each event. Here, grouping by location rather than by error cause painted a false picture of a client-side bug. The takeaway: never fix a problem based solely on a dashboard headline. Pull a sample of the underlying events and verify what’s really happening before you allocate resources.
Lesson 2 – Placeholder values are not measurements
To avoid loading the hefty runtime for users on sluggish links, the code consulted the Network Information API and read the downlink property, which reports megabits per second. On a first visit, Chrome often returns a placeholder instead of an actual measurement. The logic treated that placeholder as a fast connection and skipped the optimization, effectively blocking the very users it was meant to help.
Treat any default or sentinel value as “no data.” A placeholder should trigger a fallback strategy, not be interpreted as a real speed reading.
Lesson 3 – Network conditions change, so a single snapshot is unreliable
After the downlink issue, the team switched to checking effectiveType, which categorises connections as “4g”, “3g”, etc. A quick lab test passed, but the same test rerun moments later failed. Mobile connections fluctuate; a user can appear on a fast 4G link one second and drop to a slower 3G the next. Checking the connection only at page load is a gamble.
The proper approach is to subscribe to the change event on the Network Information object and react to any shift in bandwidth rather than making a one-off decision.
What the team changed
- Two-stage download – The runtime now starts with a tiny bootstrap file. If the connection is identified as slow, the bootstrap fetches the rest of the runtime in small chunks, reducing the chance of a complete abort.
- Live monitoring – Instead of a single
downlinkread, the code now listens forchangeevents and adjusts the download strategy on the fly. - Stable source selection – Previously the system switched CDNs mid-download when a faster endpoint appeared. On a slow link that caused the download to restart from zero, compounding the problem. The new logic locks the source for the duration of a download.
- Deferred cache writes – Heavy cache operations that ran before the app was usable are now postponed until after the runtime has started, freeing bandwidth for the critical download.
The broader stakes
For developers building web-based tools, network variability is a first-class concern. A silent failure on a slow link frustrates users and skews telemetry, leading teams down the wrong debugging path. In this case, misinterpreting data caused weeks of fruitless investigation.
What to watch next
Takeaway: When data looks too clean, it’s probably a placeholder; when a dashboard headline points to a single bug, dig deeper; and when you base a decision on a one-time network read, you’re betting on a moving target. Adjusting for these realities turns silent failures into predictable, recoverable events.
